package com.example.yijian.edit;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PointF;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.example.yijian.PreviewActivity;
import com.example.yijian.R;
import com.example.yijian.edit.background.BackgroundStyleInfo;
import com.example.yijian.edit.background.MultiColorInfo;
import com.example.yijian.edit.data.BackupData;
import com.example.yijian.edit.data.BitmapData;
import com.example.yijian.edit.data.ChangeSpeedCurveInfo;
import com.example.yijian.edit.data.mask.MaskBean;
import com.example.yijian.edit.data.mask.MaskInfoData;
import com.example.yijian.edit.mask.NvMaskHelper;
import com.example.yijian.edit.mask.view.MaskView;
import com.example.yijian.edit.mask.view.ZoomView;
import com.example.yijian.edit.timelineEditor.MultiThumbnailSequenceView;
import com.example.yijian.edit.timelineEditor.NvsTimelineEditor;
import com.example.yijian.edit.timelineEditor.NvsTimelineTimeSpanExt;
import com.example.yijian.edit.view.DragSortView;
import com.example.yijian.edit.view.BottomEditTypeView;
import com.example.yijian.edit.view.DrawRect;
import com.example.yijian.edit.view.EffectView;
import com.example.yijian.edit.view.MaskControlView;
import com.example.yijian.edit.view.WaterMarkView;
import com.example.yijian.edit.view.popup.SpeedCurveEditPopup;
import com.example.yijian.edit.watermarker.EffectItemData;
import com.example.yijian.edit.watermarker.SingleClickActivity;
import com.example.yijian.edit.watermarker.WaterMarkData;
import com.example.yijian.edit.watermarker.WaterMarkItemData;
import com.example.yijian.edit.watermarker.WaterMarkUtil;
import com.example.yijian.seletemedia.SelectMediaActivity;
import com.example.yijian.utils.AppManager;
import com.example.yijian.utils.Constants;
import com.example.yijian.utils.LogUtils;
import com.example.yijian.utils.ScreenUtils;
import com.example.yijian.utils.TimelineUtil;
import com.example.yijian.utils.Util;
import com.example.yijian.utils.asset.NvAsset;
import com.example.yijian.utils.datainfo.BackGroundInfo;
import com.example.yijian.utils.datainfo.ClipInfo;
import com.example.yijian.utils.datainfo.TimelineData;
import com.example.yijian.utils.datainfo.TransitionInfo;
import com.example.yijian.utils.datainfo.VideoFx;
import com.meicam.sdk.NvsAVFileInfo;
import com.meicam.sdk.NvsAnimatedSticker;
import com.meicam.sdk.NvsCaption;
import com.meicam.sdk.NvsLiveWindow;
import com.meicam.sdk.NvsStreamingContext;
import com.meicam.sdk.NvsTimeline;
import com.meicam.sdk.NvsTimelineCaption;
import com.meicam.sdk.NvsTimelineCompoundCaption;
import com.meicam.sdk.NvsTimelineVideoFx;
import com.meicam.sdk.NvsVideoClip;
import com.meicam.sdk.NvsVideoFx;
import com.meicam.sdk.NvsVideoResolution;
import com.meicam.sdk.NvsVideoTrack;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import razerdp.basepopup.BasePopupWindow;

import static com.example.yijian.base.NvsConstants.BLURNAME;
import static com.example.yijian.base.NvsConstants.MOSAICNAME;
import static com.example.yijian.edit.watermarker.WaterMarkConstant.WATERMARKTYPE_DYNAMICS;
import static com.example.yijian.edit.watermarker.WaterMarkConstant.WATERMARKTYPE_EFFECK;
import static com.example.yijian.edit.watermarker.WaterMarkConstant.WATERMARKTYPE_STATIC;
import static com.example.yijian.utils.Constants.BLUR;
import static com.example.yijian.utils.Constants.EDIT_MODE_EFFECT;
import static com.example.yijian.utils.Constants.EDIT_MODE_WATERMARK;
import static com.example.yijian.utils.Constants.MOSAIC;
import static com.example.yijian.utils.MediaConstant.SINGLE_PICTURE_PATH;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
public class VideoEditActivity extends AppCompatActivity implements View.OnClickListener, EditTypeListener, DragSortView.CallBack {


    private static final String TAG = "VideoEditActivity";
    private static final float DEFAULT_SCALE_VALUE = 1.0f;
    /*
     * 添加视频
     * Add video
     * */
    public static final int ADDVIDEO_REQUESTCODE = 107;
    /*
     * 添加自定义水印
     * Add Watermark
     * */
    public static final int ADDWATERMARK_REQUESTCODE = 108;

    private static final int RESETPLATBACKSTATE = 100;

    private RelativeLayout mPlayerLayout;
    private NvsLiveWindow mLiveWindow;
    private Point liveWindowPoint;
    private RelativeLayout mPlayButton;
    private ImageView mPlayImage;
    private TextView mCurrentPlayTime;
    private TextView mTotalDuration;
    private LinearLayout bottomLayout;
    private BottomEditTypeView bottomEditView;

    private ImageView ivFullScreen;

    private int mEditMode = 0;

    /*
     * liveWindow 实际view中坐标点
     * coordinate point in the actual view of liveWindow
     * */
    private List<PointF> pointFListLiveWindow;
    /*----------------------------字幕-start---------------------------*/
    private NvsCaption mCurCaption;
    private NvsTimelineCompoundCaption mCurCompoundCaption;
    /*----------------------------字幕-end---------------------------*/

    /*----------------------------贴纸-start---------------------------*/
    private NvsAnimatedSticker mCurAnimateSticker;
    /*----------------------------贴纸-end---------------------------*/

    /*----------------------------水印-start---------------------------*/
    public static final int WATER_MARK_TYPE = 0;
    private String mPicturePath;
    private int pictureType;
    private String mWaterMarkPicture;
    private WaterMarkView mWaterMarkView;
    private DrawRect mDrawRect;
    //水印类型
    private int mWaterType = 0;
    private int waterMarkType;
    private int mPictureW;
    private int mPictureH;
    private float mTransX = 0;
    private float mTransY = 0;
    private float mScale = 1;
    /*
     * 第一次添加水印时的原始坐标列表，用于计算偏移量
     * A list of the original coordinates when the watermark was first added, used to calculate the offset
     * */
    private List<PointF> pointFListToFirstAddWaterMark;
    /*----------------------------水印-end---------------------------*/

    /*----------------------------效果-start---------------------------*/
    private EffectView mEffectView;
    private boolean hasEffect;
    /*----------------------------效果-end---------------------------*/

    /*----------------------------蒙版-start---------------------------*/
    private MaskControlView mMaskControlView;
    private ZoomView zoomView;
    private MaskView maskView;
    private Map<Integer, MaskInfoData> maskInfoDataMap = new TreeMap<>();
    /*----------------------------蒙版-end---------------------------*/

    private static final float defaultVolumeValue = 1.0f;
    private static final float muteVolumeValue = 0.0f;
    private boolean mVideoIsMute = false;

    private SpeedCurveEditPopup speedCurveEditPopup;

    private boolean mPlayBarVisibleState = true, mVoiceButtonVisibleState = false, mAutoPlay = true, mRecording = false;

    private int mPlayFlag = NvsStreamingContext.STREAMING_ENGINE_PLAYBACK_FLAG_BUDDY_HOST_VIDEO_FRAME;
    /*
     * 播放开始标识
     * Play start identification
     * */
    private long mPlayStartFlag = -1;
    //动画页面调用时传入，其他位置忽略即可
    private long mPlayStartPoint = 0;
    private long mPlayEndPoint = 0;
    private int playState;
    private boolean mIsAnimationView = false;
    private boolean mIsBackgroundView = false;

    private NvsStreamingContext mStreamingContext;
    private NvsTimelineVideoFx nvsTimelineVideoFx;
    private VideoFx mVideoFx;
    private NvsTimeline mTimeline;
    private NvsVideoTrack mVideoTrack;
    private RelativeLayout timeline_edit_layout;


    private MultiThumbnailSequenceView mMultiSequenceView;
    private NvsTimelineEditor mTimelineEditor;
    private NvsTimelineTimeSpanExt mTimlineTimeSpanExt;
    ArrayList<ClipInfo> mClipInfoArray = new ArrayList<>();
    private ArrayList<TransitionInfo> mTransitionInfoArray;
    private ClipInfo mClipInfo = new ClipInfo();
    private boolean mIsSeekTimeline = true;
    private Handler mhandler=new Handler();
    private Runnable runnable;
    //-------------------常量区---------------------
    private long mTrimInPoint = 0;
    private long mTrimOutPoint = 0;
    private int mCurrentPos = -1;//当前编辑的片段
    private int mAddVideoPostion = 0;
    private float x;
    private float y;
    private boolean isLongClick;
    private int halfScreenWidth;

    private GestureDetector gestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_edit);
        halfScreenWidth = ScreenUtils.getScreenWidth(this) / 3;
        initView();
        initData();
        initTimeLineEditor();
        gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                mTimelineEditor.unSelectAllTimeSpan();
                return super.onSingleTapUp(e);
            }
        });
    }

    public void updateTotalDurationText() {
        if (mTimeline != null) {
            mTotalDuration.setText(formatTimeStrWithUs(mTimeline.getDuration()));
//            mPlaySeekBar.setMax((int) (mTimeline.getDuration() / BASE_VALUE));
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        int currEngineState = getCurrentEngineState();
        if (currEngineState != NvsStreamingContext.STREAMING_ENGINE_STATE_COMPILE) {
            connectTimelineWithLiveWindow();
            long stamp = mStreamingContext.getTimelineCurrentPosition(mTimeline);
            updateCurPlayTime(stamp);
            Log.e(TAG, "onResume");
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (mAutoPlay && mPlayImage != null) {
                        playVideoButtonClick();
                    }
                }
            }, 100);
        }
    }

    public void playVideoButtonClick() {
        if (mTimeline == null) {
            return;
        }
        long endTime = mTimeline.getDuration();
        playVideoButtonClick(0, endTime);
    }

    public void playVideoButtonClick(long inPoint, long outPoint) {
        playVideo(inPoint, outPoint);
        /*
         * 更新播放进度条显示标识
         * Update the playback progress bar indicator
         * */
        if (mPlayBarVisibleState) {
            mPlayStartFlag = mStreamingContext.getTimelineCurrentPosition(mTimeline);
//            mPlayBarLayout.setVisibility(View.VISIBLE);
        }
    }

    /*
     * 播放视频
     * Play video
     * */
    public void playVideo(long startTime, long endTime) {
        if (mPlayFlag == 0) {
            int flag = NvsStreamingContext.STREAMING_ENGINE_PLAYBACK_FLAG_SPEED_COMP_MODE;
            mStreamingContext.playbackTimeline(mTimeline, startTime, endTime, NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, true, flag);
        } else {
            mStreamingContext.playbackTimeline(mTimeline, startTime, endTime, NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, true, mPlayFlag);
        }

    }

    /*
     * 获取当前引擎状态
     * Get the current engine status
     * */
    public int getCurrentEngineState() {
        return mStreamingContext.getStreamingEngineState();
    }

    /*
     * 停止引擎
     * Stop the engine
     * */
    public void stopEngine() {
        //如果生成的时候不要调用stop，防止切换到后台被停止
        if (getCurrentEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_COMPILE) {
            if (mStreamingContext != null) {
                mStreamingContext.stop();
            }
        }
    }


    private Handler m_handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // TODO Auto-generated method stub
            switch (msg.what) {
                case RESETPLATBACKSTATE:
                    updateCurPlayTime(0);
                    seekTimeline(mPlayStartPoint, 0);
                    /*
                     * 播放进度条显示
                     * Play progress bar display
                     * */
                    if (mPlayBarVisibleState) {
                        mPlayStartFlag = -1;
                    }
                    break;
            }
            return false;
        }
    });

    /*
     * 预览
     * Seek
     * */
    public void seekTimeline(long timestamp, int seekShowMode) {
        mStreamingContext.seekTimeline(mTimeline, timestamp,
                NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, seekShowMode | NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_BUDDY_HOST_VIDEO_FRAME);

    }

    public void updateCurPlayTime(long time) {
        mCurrentPlayTime.setText(formatTimeStrWithUs(time));
//        mPlaySeekBar.setProgress((int) (time / BASE_VALUE));
    }

    //formate time
    private String formatTimeStrWithUs(long us) {
        int second = (int) (us / 1000000.0);
        int hh = second / 3600;
        int mm = second % 3600 / 60;
        int ss = second % 60;
        return hh > 0 ? String.format("%02d:%02d:%02d", hh, mm, ss) : String.format("%02d:%02d", mm, ss);
    }

    /*
     * 连接时间线跟liveWindow
     * Connect timeline with liveWindow
     * */
    public void connectTimelineWithLiveWindow() {
        if (mStreamingContext == null || mTimeline == null || mLiveWindow == null)
            return;
        mStreamingContext.setPlaybackCallback(new NvsStreamingContext.PlaybackCallback() {
            @Override
            public void onPlaybackPreloadingCompletion(NvsTimeline nvsTimeline) {

            }

            @Override
            public void onPlaybackStopped(NvsTimeline nvsTimeline) {
//                if (mVideoFragmentCallBack != null) {
//                    mVideoFragmentCallBack.playStopped(nvsTimeline);
//                }
            }

            @Override
            public void onPlaybackEOF(NvsTimeline nvsTimeline) {
//                if (mPlayBarVisibleState) {
//                    //m_handler.sendEmptyMessage(RESETPLATBACKSTATE);
//                }
                if (nvsTimeline.getDuration() - mStreamingContext.getTimelineCurrentPosition(mTimeline) <= 40000) {
                    updateCurPlayTime(0);
                    seekTimeline(0, 0);
                }
            }
        });

        mStreamingContext.setPlaybackCallback2(new NvsStreamingContext.PlaybackCallback2() {
            @Override
            public void onPlaybackTimelinePosition(NvsTimeline nvsTimeline, long cur_position) {
                updateCurPlayTime(cur_position);
//                mTimelineEditor.unSelectAllTimeSpan();
                if (mMultiSequenceView != null) {
                    int x = Math.round((cur_position / (float) mTimeline.getDuration() * mTimelineEditor.getSequenceWidth()));
                    mMultiSequenceView.smoothScrollTo(x, 0);
                }
                if (speedCurveEditPopup != null && speedCurveEditPopup.isShowing()) {
                    speedCurveEditPopup.updatePlayProgress(cur_position);
                }
//                if (mVideoFragmentCallBack != null) {
//                    mVideoFragmentCallBack.playbackTimelinePosition(nvsTimeline, cur_position);
//                }
//                /*
//                 *  播放进度条消失
//                 * The playback progress bar disappears
//                 * */
//                if (mPlayBarVisibleState) {
//                    if (mPlayStartFlag != -1) {
//                        if (cur_position - mPlayStartFlag >= 4000000)
//                            mPlayBarLayout.setVisibility(View.INVISIBLE);
//                    }
//                }
            }
        });

        mStreamingContext.setStreamingEngineCallback(new NvsStreamingContext.StreamingEngineCallback() {
            @Override
            public void onStreamingEngineStateChanged(int i) {
                playState = i;
                if (i == NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    mIsSeekTimeline = false;
                    mPlayImage.setBackgroundResource(R.mipmap.icon_play);
                } else {
                    mIsSeekTimeline = true;
                    mPlayImage.setBackgroundResource(R.mipmap.icon_pause);
                }
            }

            @Override
            public void onFirstVideoFramePresented(NvsTimeline nvsTimeline) {

            }
        });

        mStreamingContext.setHardwareErrorCallback(new NvsStreamingContext.HardwareErrorCallback() {
            @Override
            public void onHardwareError(int errorType, String stringInfo) {
                Log.e(TAG, "onHardwareError: errorType: " + errorType + " stringInfo: " + stringInfo);
            }
        });

        mStreamingContext.connectTimelineWithLiveWindow(mTimeline, mLiveWindow);
    }


    private void initData() {
        mStreamingContext = NvsStreamingContext.getInstance();
        mTimeline = TimelineUtil.createTimeline();
        mTransitionInfoArray = TimelineData.instance().cloneTransitionsData();
        mClipInfoArray = TimelineData.instance().cloneClipInfoData();
        BackupData.instance().setClipIndex(0);
        BackupData.instance().setClipInfoData(mClipInfoArray);
        if (mTimeline == null) {
            return;
        }
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mVideoTrack == null) {
            return;
        }
        updateClipInfo();
        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        setLiveWindowRatio(TimelineData.instance().getMakeRatio(), 0, bottomLayout.getLayoutParams().height);
        updateTotalDurationText();
    }

    private void updateClipInfo() {
        for (int index = 0; index < mVideoTrack.getClipCount(); index++) {
            long trimIn = mClipInfoArray.get(index).getTrimIn();
            if (trimIn < 0)
                mClipInfoArray.get(index).changeTrimIn(mVideoTrack.getClipByIndex(index).getTrimIn());
            long trimOut = mClipInfoArray.get(index).getTrimOut();
            if (trimOut < 0)
                mClipInfoArray.get(index).changeTrimOut(mVideoTrack.getClipByIndex(index).getTrimOut());
        }
    }

    private void initView() {
        mPlayerLayout = (RelativeLayout) findViewById(R.id.video_layout);
        mLiveWindow = findViewById(R.id.liveWindow);
        mPlayButton = findViewById(R.id.play_layout);
        mPlayImage = findViewById(R.id.iv_play);
        mCurrentPlayTime = findViewById(R.id.currentPlaytime);
        mTotalDuration = findViewById(R.id.totalDuration);
        bottomLayout = findViewById(R.id.bottom_layout);
        mTimelineEditor = findViewById(R.id.timelineEditor);
        timeline_edit_layout=findViewById(R.id.timeline_edit_layout);
        bottomEditView = findViewById(R.id.bottom_edit_view);
        ivFullScreen = findViewById(R.id.iv_full_screen);
        initWaterMarkView();
        initEffectView();
        initMaskView();
        findViewById(R.id.iv_edit_close).setOnClickListener(this);
        findViewById(R.id.iv_add_video).setOnClickListener(this);
        findViewById(R.id.iv_full_screen).setOnClickListener(this);
        controllerOperation();
        bottomEditView.setEditListener(this);

    }

    private void initEffectView() {
        mEffectView = findViewById(R.id.effect_view);
        mEffectView.setOnItemClickListener(new EffectView.OnItemClickListener() {
            @Override
            public void onEffectClick(int position, EffectItemData effectItemData) {
                if (position == 0) {
                    removeWaterToTimeline();
                    return;
                }
                waterMarkType = WATERMARKTYPE_EFFECK;
                clearAllWaterMarkData(waterMarkType);
                if (effectItemData != null) {
                    hasEffect = true;
                    int type = effectItemData.getType();
                    mEditMode = EDIT_MODE_EFFECT;
                    setEffectByPoint(type);
                    mDrawRect.setVisibility(View.VISIBLE);
                    refreshLiveWindowFrame();
                }
            }

            @Override
            public void onDismiss() {
                if (mEffectView != null) {
                    mEffectView.refreshSelectPosition();
                }
                updateWaterMarkOrEffect(waterMarkType);
            }
        });
        mEffectView.setOnEffectSeekBarListener(new EffectView.OnEffectSeekBarListener() {
            @Override
            public void onViewLevelChange(float level) {
                if (mVideoFx != null) {
                    mVideoFx.setIntensity(level);
                    nvsTimelineVideoFx.setFilterIntensity(level);
                    refreshLiveWindowFrame();
                }
            }

            @Override
            public void onViewNumberChange(float num) {
                if (mVideoFx != null) {
                    mVideoFx.setUnitSize(num);
                    nvsTimelineVideoFx.setFloatVal("Unit Size", num);
                    refreshLiveWindowFrame();
                }

            }

            @Override
            public void onViewBlurChange(float level) {
                if (mVideoFx != null) {
                    mVideoFx.setRadius(level);
                    nvsTimelineVideoFx.setFloatVal("Radius", level);
                    refreshLiveWindowFrame();
                }
            }
        });
    }

    /**
     * 根据type设置特效
     * Set special effects according to type
     *
     * @param type
     */
    public void setEffectByPoint(int type) {
        setPointFListLiveWindow(mLiveWindow.getWidth(), mLiveWindow.getHeight());
        setEffectPoint(type);
        //如果存在获取原特效隐藏，不存在走创建
        //If there is to get the original special effect to hide, there is no to create it
        if (mTimeline == null) {
            return;
        }
        if (mDrawRect != null) {
            mDrawRect.setPicturePath("");
        }
        nvsTimelineVideoFx = null;
        NvsTimelineVideoFx timelineVideoFx = mTimeline.getFirstTimelineVideoFx();
        while (timelineVideoFx != null) {
            if (timelineVideoFx.getTimelineVideoFxType() == NvsVideoFx.VIDEOFX_TYPE_BUILTIN) {
                String fxName = timelineVideoFx.getBuiltinTimelineVideoFxName();
                if (TextUtils.equals(MOSAICNAME, fxName) ||
                        TextUtils.equals(BLURNAME, fxName)) {
                    timelineVideoFx.setFilterIntensity(0);
                    if (TextUtils.equals(MOSAICNAME, fxName) && type == EffectItemData.TYPE_MOSAIC) {
                        nvsTimelineVideoFx = timelineVideoFx;
                        timelineVideoFx.setFilterIntensity(1);
                    } else if (TextUtils.equals(BLURNAME, fxName) && type == EffectItemData.TYPE_BLUR) {
                        nvsTimelineVideoFx = timelineVideoFx;
                        timelineVideoFx.setFilterIntensity(1);
                    }
                }
            }
            timelineVideoFx = mTimeline.getNextTimelineVideoFx(timelineVideoFx);
        }
        mWaterType = type;
        if (nvsTimelineVideoFx == null) {
            if (type == EffectItemData.TYPE_MOSAIC) {
                nvsTimelineVideoFx = mTimeline.addBuiltinTimelineVideoFx(0, mTimeline.getDuration(), MOSAICNAME);
                nvsTimelineVideoFx.setRegional(true);
                nvsTimelineVideoFx.setRegionalFeatherWidth(0f);
                nvsTimelineVideoFx.setFloatVal("Unit Size", 0.1f);
                nvsTimelineVideoFx.setFilterIntensity(1);
                float[] fx = getFxData();
                nvsTimelineVideoFx.setRegion(fx);
                mVideoFx = new VideoFx();
                mVideoFx.setRegion(fx);
                mVideoFx.setIntensity(1);
                mVideoFx.setUnitSize(0.1f);
                mVideoFx.setDesc(MOSAICNAME);
                mVideoFx.setType(String.valueOf(type));
            } else if (type == EffectItemData.TYPE_BLUR) {
                nvsTimelineVideoFx = mTimeline.addBuiltinTimelineVideoFx(0, mTimeline.getDuration(), BLURNAME);
                nvsTimelineVideoFx.setRegional(true);
                float[] fx = getFxData();
                nvsTimelineVideoFx.setRegion(fx);
                nvsTimelineVideoFx.setRegionalFeatherWidth(0f);
                nvsTimelineVideoFx.setFloatVal("Radius", 64.0f);
                nvsTimelineVideoFx.setFilterIntensity(1);
                mVideoFx = new VideoFx();
                mVideoFx.setRegion(fx);
                mVideoFx.setIntensity(1);
                mVideoFx.setRadius(64.0f);
                mVideoFx.setDesc(BLURNAME);
                mVideoFx.setType(String.valueOf(type));
                TimelineData.instance().setTimelineVideoFx(mVideoFx);
            }
        } else {
            // 设置mDrawRect位置和尺寸
            // Set mDrawRect position and size
            if (type == EffectItemData.TYPE_MOSAIC) {
                float[] fx = nvsTimelineVideoFx.getRegion();
                mVideoFx = new VideoFx();
                mVideoFx.setRegion(fx);
                mVideoFx.setIntensity(1);
                mVideoFx.setUnitSize((float) nvsTimelineVideoFx.getFloatVal("Unit Size"));
                mVideoFx.setDesc(MOSAICNAME);
                mVideoFx.setType(String.valueOf(type));
            } else if (type == EffectItemData.TYPE_BLUR) {
                float[] fx = nvsTimelineVideoFx.getRegion();
                mVideoFx = new VideoFx();
                mVideoFx.setRegion(fx);
                mVideoFx.setIntensity(1);
                mVideoFx.setRadius((float) nvsTimelineVideoFx.getFloatVal("Radius"));
                mVideoFx.setDesc(BLURNAME);
                mVideoFx.setType(String.valueOf(type));
                TimelineData.instance().setTimelineVideoFx(mVideoFx);
            }
            setDrawRectByEffectData(nvsTimelineVideoFx, type);
        }

        TimelineData.instance().setTimelineVideoFx(mVideoFx);
        //刷新 mRectView位置
        //refresh mRectView position
        refreshLiveWindowFrame();
    }

    /**
     * 根据效果特效位置来更新mDrawRect位置
     * Update the position of mDrawRect according to the position of the effect
     */
    private void setDrawRectByEffectData(NvsTimelineVideoFx videoFx, int type) {
        float[] region = videoFx.getRegion();
        if (region.length >= 8) {
            List<PointF> pointFS = new ArrayList<>();
            pointFS.add(mLiveWindow.mapNormalizedToView(new PointF(region[0], region[1])));
            pointFS.add(mLiveWindow.mapNormalizedToView(new PointF(region[2], region[3])));
            pointFS.add(mLiveWindow.mapNormalizedToView(new PointF(region[4], region[5])));
            pointFS.add(mLiveWindow.mapNormalizedToView(new PointF(region[6], region[7])));
            mDrawRect.setDrawRect(pointFS, type);
        }

    }

    /**
     * 删除模糊,马赛克
     * Remove blur, mosaic
     */
    public void removeWaterToTimeline() {
        if (nvsTimelineVideoFx != null) {
            mTimeline.removeTimelineVideoFx(nvsTimelineVideoFx);
            TimelineData.instance().clearTimelineVideoFx();
        }
        mDrawRect.setVisibility(View.GONE);
        refreshLiveWindowFrame();
    }

    private void initWaterMarkView() {
        mWaterMarkView = findViewById(R.id.watermark_view);
        mDrawRect = findViewById(R.id.draw_rect);
        initDrawRectListener();
        mWaterMarkView.setItemClickListener(new WaterMarkView.OnWaterMarkItemClickListener() {
            @Override
            public void onItemClick(int position, int mPictureType, String picturePath, int waterMarkType, String waterMarkPicture) {
                if (position == 0 && waterMarkType == WATERMARKTYPE_STATIC) {
//                    backFromSelect = true;
                    AppManager.getInstance().jumpActivityForResult(VideoEditActivity.this, SingleClickActivity.class, new Bundle(), VideoEditActivity.ADDWATERMARK_REQUESTCODE);
                } else {
                    clearAllWaterMarkData(waterMarkType);
                    pictureType = mPictureType;
                    setWaterMarkData(waterMarkType, picturePath, waterMarkPicture);
                    setWaterMarkOnTheFirst(waterMarkPicture);
                }
                refreshLiveWindowFrame();
            }

            @Override
            public void onDismiss() {
                updateWaterMarkOrEffect(waterMarkType);
            }
        });
    }

    private void updateWaterMarkOrEffect(int waterMarkType) {
        mDrawRect.setPicturePath(null);
        mDrawRect.setVisibility(View.GONE);
        if (waterMarkType == WATERMARKTYPE_STATIC) {
            if (mPicturePath != null) {
                List<PointF> nowPointToRect = mDrawRect.getDrawRect();
                List<PointF> newPointToRect = viewPointToCanonical(nowPointToRect);
                List<PointF> pointAtFirst = getPointFListToFirstAddWaterMark();
                List<PointF> newPointAtFirst = viewPointToCanonical(pointAtFirst);
                int excursionX = (int) (newPointAtFirst.get(3).x - newPointToRect.get(3).x);
                int excursionY = (int) (newPointAtFirst.get(3).y - newPointToRect.get(3).y);
                int picWidthOfCanonical = (int) Math.abs(newPointToRect.get(0).x - newPointToRect.get(3).x);
                int picHeightOfCanonical = (int) Math.abs(newPointToRect.get(0).y - newPointToRect.get(1).y);
                if (this.waterMarkType == WATERMARKTYPE_DYNAMICS) {
                    picWidthOfCanonical = (int) Math.abs(nowPointToRect.get(0).x - nowPointToRect.get(3).x);
                    picHeightOfCanonical = (int) Math.abs(nowPointToRect.get(0).y - nowPointToRect.get(1).y);
                }

                WaterMarkData waterMarkData = new WaterMarkData(nowPointToRect, excursionX, excursionY, picWidthOfCanonical,
                        picHeightOfCanonical, mPicturePath, new Point(mLiveWindow.getWidth(), mLiveWindow.getHeight()), new WaterMarkItemData(pictureType, this.waterMarkType, mWaterMarkPicture, mPicturePath));
                waterMarkData.setScale(mScale);
                waterMarkData.setTransX(mTransX);
                waterMarkData.setTransY(mTransY);
                TimelineData.instance().setWaterMarkData(waterMarkData);
                if (!hasEffect) {
                    TimelineData.instance().clearTimelineVideoFx();
                }
                WaterMarkUtil.setWaterMark(mTimeline, TimelineData.instance().getWaterMarkData());
            } else {
                TimelineData.instance().cleanWaterMarkData();
                mTimeline.deleteWatermark();
                if (!hasEffect) {
                    TimelineData.instance().clearTimelineVideoFx();
                }
            }
        }
        if (!hasEffect) {
            NvsTimelineVideoFx lastFx = mTimeline.getLastTimelineVideoFx();
            while (lastFx != null) {
                String fxName = lastFx.getBuiltinTimelineVideoFxName();
                if (TextUtils.equals(fxName, "Mosaic")
                        || TextUtils.equals(fxName, "Gaussian Blur")) {
                    mTimeline.removeTimelineVideoFx(lastFx);
                    break;
                }
                lastFx = mTimeline.getPrevTimelineVideoFx(lastFx);
            }
        }
        refreshLiveWindowFrame();
        TimelineUtil.checkAndDeleteExitFX(mTimeline);
        VideoFx videoFx = TimelineData.instance().getVideoFx();
        setEffectByData(videoFx);
    }

    /**
     * 从VideoFx中获取信息并设置
     * Get information from VideoFx and set
     *
     * @param videoFx
     */
    public void setEffectByData(VideoFx videoFx) {
        if (videoFx == null) {
            return;
        }

        int type = Integer.valueOf(videoFx.getType());
        setPointFListLiveWindow(mLiveWindow.getWidth(), mLiveWindow.getHeight());
        setEffectPoint(type);
        mWaterType = type;

        mTimeline.removeTimelineVideoFx(nvsTimelineVideoFx);
        if (type == EffectItemData.TYPE_MOSAIC) {
            nvsTimelineVideoFx = mTimeline.addBuiltinTimelineVideoFx(0, mTimeline.getDuration(), MOSAICNAME);
            nvsTimelineVideoFx.setFilterIntensity(videoFx.getIntensity());
            nvsTimelineVideoFx.setRegional(true);
            nvsTimelineVideoFx.setRegion(videoFx.getRegion());
            nvsTimelineVideoFx.setRegionalFeatherWidth(0f);
            nvsTimelineVideoFx.setFloatVal("Unit Size", videoFx.getUnitSize());

        } else if (type == EffectItemData.TYPE_BLUR) {
            nvsTimelineVideoFx = mTimeline.addBuiltinTimelineVideoFx(0, mTimeline.getDuration(), BLURNAME);
            nvsTimelineVideoFx.setFilterIntensity(videoFx.getIntensity());
            nvsTimelineVideoFx.setRegional(true);
            nvsTimelineVideoFx.setRegion(videoFx.getRegion());
            nvsTimelineVideoFx.setRegionalFeatherWidth(0f);
            nvsTimelineVideoFx.setFloatVal("Radius", videoFx.getRadius());

        }
        refreshLiveWindowFrame();
    }


    /**
     * 设置特效类型
     * set effect type
     *
     * @param type
     */
    private void setEffectPoint(int type) {
        int screenWidth = mLiveWindow.getWidth();//屏宽A
        int height = mLiveWindow.getHeight();
        int defaultWidth = (int) getResources().getDimension(R.dimen.edit_waterMark_width);
        int defaultHeight = (int) getResources().getDimension(R.dimen.edit_waterMark_height);
        setEffectDrawableByPath(screenWidth / 2 - defaultWidth / 2, height / 2 - defaultHeight / 2, type);
    }

    public void setEffectDrawableByPath(int left, int top, int type) {
        mDrawRect.setEffectBitmapByImgPath(left, top, type);
    }


    private void initDrawRectListener() {
        mDrawRect.setOnTouchListener(new DrawRect.OnTouchListener() {
            @Override
            public void onDrag(PointF prePointF, PointF nowPointF) {

                /* 坐标转换
                 *
                 * SDK接口所使用的坐标均是Canonical坐标系内的坐标，而我们在程序中所用是的
                 * 一般是Android View 坐标系里面的坐标，所以在使用接口的时候需要使用SDK所
                 * 提供的mapViewToCanonical函数将View坐标转换为Canonical坐标，相反的，
                 * 如果想要将Canonical坐标转换为View坐标，则可以使用mapCanonicalToView函数进行转换。
                 *
                 *
                 * Coordinate transformation
                 * The coordinates used by the DK interface are the coordinates in the Canonical coordinate system,
                 * and the coordinates we use in the program are generally the coordinates in the Android View coordinate system,
                 * so when using the interface, you need to use the mapViewToCanonical function provided by the SDK to view the coordinates Convert to Canonical coordinates.
                 * Conversely, if you want to convert Canonical coordinates to View coordinates, you can use the mapCanonicalToView function to convert them.
                 * */
                //拖动前停止播放
                if (mStreamingContext.getStreamingEngineState() == NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    mStreamingContext.stop();
                }
                PointF pre = mLiveWindow.mapViewToCanonical(prePointF);
                PointF p = mLiveWindow.mapViewToCanonical(nowPointF);

                PointF timeLinePointF = new PointF(p.x - pre.x, p.y - pre.y);

                if (mEditMode == Constants.EDIT_MODE_CAPTION) {
                    /*
                     * 移动字幕
                     * Moving captions
                     * */
                    if (mCurCaption != null) {
//                        if (mBeforeAnimateStickerEditListener != null) {
//                            boolean b = mBeforeAnimateStickerEditListener.beforeTransitionCouldDo();
//                            if (!b) {
//                                return;
//                            }
//                        }

                        //mTimeline.setupInputCacheForCaption(mCurCaption,mStreamingContext.getTimelineCurrentPosition(mTimeline));//解决拖拽字幕跟不上拖拽框的问题
                        mCurCaption.translateCaption(timeLinePointF);
//                        updateCaptionCoordinate(mCurCaption);
                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
                    }
                } else if (mEditMode == Constants.EDIT_MODE_STICKER) {
                    /*
                     * 移动贴纸
                     * Mobile stickers
                     * */
                    if (mCurAnimateSticker != null) {
//                        if (mBeforeAnimateStickerEditListener != null) {
//                            boolean b = mBeforeAnimateStickerEditListener.beforeTransitionCouldDo();
//                            if (!b) {
//                                return;
//                            }
//                        }
                        mCurAnimateSticker.translateAnimatedSticker(timeLinePointF);
//                        updateAnimateStickerCoordinate(mCurAnimateSticker);
                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER);
                    }
                } else if (mEditMode == EDIT_MODE_WATERMARK) {
                    if (!checkInLiveWindow(mDrawRect.getDrawRect())) {
                        return;
                    }
                    updateWaterMarkPositionOnDrag(nowPointF.x - prePointF.x, prePointF.y - nowPointF.y, mDrawRect.getDrawRect());

                } else if (mEditMode == EDIT_MODE_EFFECT) {
                    if (!checkInLiveWindow(mDrawRect.getDrawRect())) {
                        return;
                    }
                    updateEffectPositionOnDrag(nowPointF.x - prePointF.x, prePointF.y - nowPointF.y, mDrawRect.getDrawRect());

                    switch (mWaterType) {
                        case MOSAIC:
                        case BLUR:
                            float[] fx = getFxData();
                            nvsTimelineVideoFx.setRegion(fx);
                            refreshLiveWindowFrame();
                            VideoFx videoFx = TimelineData.instance().getVideoFx();
                            if (videoFx != null) {
                                videoFx.setRegion(fx);
                            }
                            break;
                    }

                } else if (mEditMode == Constants.EDIT_MODE_COMPOUND_CAPTION) {
                    if (mCurCompoundCaption != null) {
                        mCurCompoundCaption.translateCaption(timeLinePointF);
//                        updateCompoundCaptionCoordinate(mCurCompoundCaption);
                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
                    }
                }
            }

            @Override
            public void onScaleAndRotate(float scaleFactor, PointF anchor, float angle) {
                /* 坐标转换
                 *
                 * SDK接口所使用的坐标均是Canonical坐标系内的坐标，而我们在程序中所用是的
                 * 一般是Android View 坐标系里面的坐标，所以在使用接口的时候需要使用SDK所
                 * 提供的mapViewToCanonical函数将View坐标转换为Canonical坐标，相反的，
                 * 如果想要将Canonical坐标转换为View坐标，则可以使用mapCanonicalToView函数进行转换。
                 *
                 *
                 * Coordinate transformation
                 * The coordinates used by the DK interface are the coordinates in the Canonical coordinate system,
                 * and the coordinates we use in the program are generally the coordinates in the Android View coordinate system,
                 * so when using the interface, you need to use the mapViewToCanonical function provided by the SDK to view the coordinates Convert to Canonical coordinates.
                 * Conversely, if you want to convert Canonical coordinates to View coordinates, you can use the mapCanonicalToView function to convert them.
                 * */
                PointF assetAnchor = mLiveWindow.mapViewToCanonical(anchor);
                if (mEditMode == Constants.EDIT_MODE_CAPTION) {
//                    if (mCurCaption != null) {
//                        if (mBeforeAnimateStickerEditListener != null) {
//                            boolean b = mBeforeAnimateStickerEditListener.beforeScaleCouldDo();
//                            if (!b) {
//                                return;
//                            }
//                        }
//
//                        //mTimeline.setupInputCacheForCaption(mCurCaption,mStreamingContext.getTimelineCurrentPosition(mTimeline));//解决拖拽字幕跟不上拖拽框的问题
//                        /*
//                         * 放缩字幕
//                         * Shrink captions
//                         * */
//                        mCurCaption.scaleCaption(scaleFactor, assetAnchor);
//                        /*
//                         * 旋转字幕
//                         * Spin subtitles
//                         * */
//                        mCurCaption.rotateCaption(angle);
//                        updateCaptionCoordinate(mCurCaption);
//                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
//                    }
                } else if (mEditMode == Constants.EDIT_MODE_STICKER) {
//                    /*
//                     *  放缩贴纸
//                     * Scale stickers
//                     * */
//                    if (mCurAnimateSticker != null) {
//                        if (mBeforeAnimateStickerEditListener != null) {
//                            boolean b = mBeforeAnimateStickerEditListener.beforeScaleCouldDo();
//                            if (!b) {
//                                return;
//                            }
//                        }
//                        mCurAnimateSticker.scaleAnimatedSticker(scaleFactor, assetAnchor);
//                        /*
//                         * 旋转贴纸
//                         * Rotate stickers
//                         * */
//                        mCurAnimateSticker.rotateAnimatedSticker(angle);
//                        updateAnimateStickerCoordinate(mCurAnimateSticker);
//                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER);
//                    }
                } else if (mEditMode == EDIT_MODE_WATERMARK) {
                    updateWaterMarkPositionOnScaleAndRotate(scaleFactor, anchor, angle, mDrawRect.getDrawRect());
                } else if (mEditMode == EDIT_MODE_EFFECT) {
                    updateEffectPositionOnScaleAndRotate(scaleFactor, anchor, angle, mDrawRect.getDrawRect());
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER);
                } else if (mEditMode == Constants.EDIT_MODE_COMPOUND_CAPTION) {
                    if (mCurCompoundCaption != null) {
                        mCurCompoundCaption.scaleCaption(scaleFactor, assetAnchor);
                        /*
                         * 旋转字幕
                         * Spin subtitles
                         * */
                        mCurCompoundCaption.rotateCaption(angle, assetAnchor);
                        float scaleX = mCurCompoundCaption.getScaleX();
                        float scaleY = mCurCompoundCaption.getScaleY();
                        if (scaleX <= DEFAULT_SCALE_VALUE && scaleY <= DEFAULT_SCALE_VALUE) {
                            mCurCompoundCaption.setScaleX(DEFAULT_SCALE_VALUE);
                            mCurCompoundCaption.setScaleY(DEFAULT_SCALE_VALUE);
                        }
                        updateCompoundCaptionCoordinate(mCurCompoundCaption);
                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
                    }
                }
            }

            @Override
            public void onScaleXandY(float xScaleFactor, float yScaleFactor, PointF anchor) {
                if (mEditMode == EDIT_MODE_EFFECT) {
                    switch (mWaterType) {
                        case MOSAIC:
                        case BLUR:
                            if (mDrawRect != null) {
                                mDrawRect.setPicturePath("");
                            }
                            updateWaterMarkPositionOnXAndY(xScaleFactor, yScaleFactor, anchor, mDrawRect.getDrawRect());
                            float[] fx = getFxData();
                            nvsTimelineVideoFx.setRegion(fx);
                            VideoFx videoFx = TimelineData.instance().getVideoFx();
                            if (videoFx != null) {
                                videoFx.setRegion(fx);
                            }
                            refreshLiveWindowFrame();
                            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER);
                            break;
                    }
                }
            }

            @Override
            public void onDel() {
                if (mWaterType == WATER_MARK_TYPE) {
                    clearSelectedWaterMarkData();
                    if (waterMarkType == WATERMARKTYPE_DYNAMICS) {
                        TimelineUtil.checkAndDeleteExitFX(mTimeline);
                    }
                    refreshLiveWindowFrame();
                    mDrawRect.setPicturePath(null);
                    mDrawRect.setVisibility(View.GONE);
                    if (mWaterMarkView != null && mWaterMarkView.getNowPosition() != -1) {
                        mWaterMarkView.refreshNowPosition();
                    }
                } else {
                    hasEffect = false;
                    removeWaterToTimeline();
                }
                if (mEffectView != null) {
                    mEffectView.refreshSelectPosition();
                }
                waterMarkType = -1;
            }

            @Override
            public void onTouchDown(PointF curPoint) {
            }

            @Override
            public void onAlignClick(boolean isHorizontal) {
                if (mEditMode == Constants.EDIT_MODE_CAPTION
                        && mCurCaption != null) {
                    switch (mCurCaption.getTextAlignment()) {
                        case NvsTimelineCaption.TEXT_ALIGNMENT_LEFT:
                            mCurCaption.setTextAlignment(NvsTimelineCaption.TEXT_ALIGNMENT_CENTER);  //居中对齐
                            mDrawRect.setAlignIndex(1);
                            break;
                        case NvsTimelineCaption.TEXT_ALIGNMENT_CENTER:
                            mCurCaption.setTextAlignment(NvsTimelineCaption.TEXT_ALIGNMENT_RIGHT);  //居右/上对齐
                            mDrawRect.setAlignIndex(2);
                            break;
                        case NvsTimelineCaption.TEXT_ALIGNMENT_RIGHT:
                            mCurCaption.setTextAlignment(NvsTimelineCaption.TEXT_ALIGNMENT_LEFT);  //左或者下对齐
                            mDrawRect.setAlignIndex(0);
                            break;
                    }
                    updateCaptionCoordinate(mCurCaption);
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
//                    if (mAssetEditListener != null) {
//                        mAssetEditListener.onAssetAlign(mCurCaption.getTextAlignment());
//                    }
                }
            }

            @Override
            public void onOrientationChange(boolean isHorizontal) {
                if (mEditMode == Constants.EDIT_MODE_CAPTION) {
                    //切换横竖字幕
                    if (mCurCaption != null) {
                        mCurCaption.setVerticalLayout(!isHorizontal);
                        updateCaptionCoordinate(mCurCaption);
                        seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
                    }
//                    if (mAssetEditListener != null) {
//                        mAssetEditListener.onOrientationChange(isHorizontal);
//                    }
                }
            }

            @Override
            public void onHorizontalFlipClick() {
                if (mEditMode == Constants.EDIT_MODE_STICKER) {
                    if (mCurAnimateSticker == null)
                        return;
                    /*
                     * 贴纸水平翻转
                     * Sticker flips horizontally
                     * */
                    boolean isHorizontalFlip = !mCurAnimateSticker.getHorizontalFlip();
                    mCurAnimateSticker.setHorizontalFlip(isHorizontalFlip);
                    updateAnimateStickerCoordinate(mCurAnimateSticker);
                    seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_ANIMATED_STICKER_POSTER);
//                    if (mAssetEditListener != null) {
//                        mAssetEditListener.onAssetHorizFlip(isHorizontalFlip);
//                    }
                }
            }

            @Override
            public void onBeyondDrawRectClick() {
                mPlayButton.callOnClick();
            }

            @Override
            public void onTouchUp(PointF pointF) {

            }
        });


        mDrawRect.setDrawRectClickListener(new DrawRect.onDrawRectClickListener() {
            @Override
            public void onDrawRectClick(int captionIndex) {
//                if (mEditMode == Constants.EDIT_MODE_CAPTION) {
//                    if (mCaptionTextEditListener != null) {
//                        mCaptionTextEditListener.onCaptionTextEdit();
//                    }
//                } else if (mEditMode == Constants.EDIT_MODE_COMPOUND_CAPTION) {
//                    if (mCompoundCaptionListener != null) {
//                        mCompoundCaptionListener.onCaptionIndex(captionIndex);
//                    }
//                }
            }

            @Override
            public void onDrawRectDoubleClick(int captionIndex) {
                if (mEditMode == Constants.EDIT_MODE_COMPOUND_CAPTION) {
//                    if (mCompoundCaptionListener != null) {
//                        mCompoundCaptionListener.onCaptionDoubleClick(captionIndex);
//                    }
                }
            }
        });

        mDrawRect.setStickerMuteListenser(new DrawRect.onStickerMuteListenser() {
            @Override
            public void onStickerMute() {
//                if (mCurAnimateSticker == null)
//                    return;
//                mStickerMuteIndex = mStickerMuteIndex == 0 ? 1 : 0;
//                float volumeGain = mStickerMuteIndex == 0 ? 1.0f : 0.0f;
//                mCurAnimateSticker.setVolumeGain(volumeGain, volumeGain);
//                setStickerMuteIndex(mStickerMuteIndex);
//                if (mStickerMuteListener != null)
//                    mStickerMuteListener.onStickerMute();
            }
        });

//        mTransformView.setOnPipTouchListener(new TransformView.OnTransformTouchEventListener() {
//            @Override
//            public void onTouchDown(PointF curPoint) {
//
//            }
//
//            @Override
//            public void onScaleAndRotate(float scale, float degree) {
//                if ((scale == 1.0f) && (degree == 0.0f)) {
//                    return;
//                }
//
//                if (mSelectClipInfo == null) {
//                    return;
//                }
//
//                BackGroundInfo backGroundInfo = getBackGroundInfo();
////                Map<String, Float> transform = getTransform();
////                if (transform == null) {
////                    return;
////                }
//                if (backGroundInfo == null) return;
//
//                float oldScale = backGroundInfo.getScaleX();
//                float newScale = oldScale * scale;
//                if (newScale > BACKGROUND_MAX_SCALE) {
//                    newScale = BACKGROUND_MAX_SCALE;
//                }
//                if (newScale < BACKGROUND_MIM_SCALE) {
//                    newScale = BACKGROUND_MIM_SCALE;
//                }
////                transform.put(STORYBOARD_KEY_SCALE_X, newScale);
////                transform.put(STORYBOARD_KEY_SCALE_Y, newScale);
////                float oldDegree = transform.get(STORYBOARD_KEY_ROTATION_Z);
//                float oldDegree = backGroundInfo.getRotation();
//                double newDegree = oldDegree + degree;
////                transform.put(STORYBOARD_KEY_ROTATION_Z, (float) newDegree);
//                if (backGroundInfo != null) {
//                    backGroundInfo.setScaleX(newScale);
//                    backGroundInfo.setScaleY(newScale);
//                    backGroundInfo.setRotation((float) newDegree);
//                }
//                if (mOnBackgroundChangedListener != null) {
//                    mOnBackgroundChangedListener.onBackgroundChanged();
//                }
//
//            }
//
//            @Override
//            public void onDrag(PointF prePointF, PointF nowPointF) {
//                if (mSelectClipInfo == null) {
//                    return;
//                }
//
//                float transX = nowPointF.x - prePointF.x;
//                float transY = nowPointF.y - prePointF.y;
//                if ((transX == 0) && (transY == 0)) {
//                    return;
//                }
//                float actualTransX = mPixTimeLineRatio * transX;
//                float actualTransY = mPixTimeLineRatio * transY;
//                BackGroundInfo backGroundInfo = getBackGroundInfo();
//                if (backGroundInfo == null) return;
////                Map<String, Float> transform = getTransform();
////                if (transform == null) {
////                    return;
////                }
////                float oldTransX = transform.get(STORYBOARD_KEY_TRANS_X);
////                float oldTransY = transform.get(STORYBOARD_KEY_TRANS_Y);
//                float oldTransX = backGroundInfo.getTransX();
//                float oldTransY = backGroundInfo.getTransY();
//                double newTransX = oldTransX + actualTransX;
//                double newTransY = oldTransY - actualTransY;
////                transform.put(STORYBOARD_KEY_TRANS_X, (float) newTransX);
////                transform.put(STORYBOARD_KEY_TRANS_Y, (float) newTransY);
//                Log.d(TAG, "onDrag x:" + newTransX + " y:" + newTransY);
//                if (backGroundInfo != null) {
//                    backGroundInfo.setTransX((float) newTransX);
//                    backGroundInfo.setTransY((float) newTransY);
//                }
//                if (mOnBackgroundChangedListener != null) {
//                    mOnBackgroundChangedListener.onBackgroundChanged();
//                }
//            }
//
//            @Override
//            public void onTouchUp(PointF curPoint) {
//
//            }
//        });
    }

    public List<PointF> getPointFListToFirstAddWaterMark() {
        if (pointFListToFirstAddWaterMark == null) {
            return new ArrayList<>();
        }
        return pointFListToFirstAddWaterMark;
    }

    /**
     * view点位坐标映射为timeline坐标
     * The view point coordinates are mapped to timeline coordinates
     *
     * @param nowPointToRect
     * @return
     */
    private List<PointF> viewPointToCanonical(List<PointF> nowPointToRect) {
        List<PointF> newPointFS = new ArrayList<>();
        for (PointF pointF : nowPointToRect) {
            PointF newPointToCanonical = mLiveWindow.mapViewToCanonical(pointF);
            newPointFS.add(newPointToCanonical);
        }
        return newPointFS;
    }

    /**
     * 更新贴纸在视图上的坐标
     * Update the coordinates of the sticker on the view
     */
    public void updateAnimateStickerCoordinate(NvsAnimatedSticker animateSticker) {
        if (animateSticker != null) {
            /*
             * 获取贴纸的原始包围矩形框变换后的顶点位置
             * Get the transformed vertex position of the original bounding rectangle of the sticker
             * */
            List<PointF> list = animateSticker.getBoundingRectangleVertices();
            if (list == null || list.size() < 4)
                return;
            boolean isHorizonFlip = animateSticker.getHorizontalFlip();
            if (isHorizonFlip) {
                /*
                 * 如果已水平翻转，需要对顶点数据进行处理
                 * If flipped horizontally, you need to process the vertex data
                 * */
                Collections.swap(list, 0, 3);
                Collections.swap(list, 1, 2);
            }
            List<PointF> newList = getAssetViewVerticesList(list);
            mDrawRect.setDrawRect(newList, Constants.EDIT_MODE_STICKER);
        }
    }

    /**
     * 更新字幕在视图上的坐标
     * Update the coordinates of the subtitle on the view
     */
    public void updateCaptionCoordinate(NvsCaption caption) {
        if (caption != null) {
            /*
             * 获取字幕的原始包围矩形框变换后的顶点位置
             * Get the transformed vertex position of the original bounding rectangle of the caption
             * */
            List<PointF> list = caption.getBoundingRectangleVertices();
            if (list == null || list.size() < 4)
                return;

            List<PointF> newList = getAssetViewVerticesList(list);
            mDrawRect.setHorizontal(!caption.getVerticalLayout());
            mDrawRect.setDrawRect(newList, Constants.EDIT_MODE_CAPTION);
        }
    }

    /**
     * @param xScaleDregree x缩放比例
     * @param yScaleDregree y缩放比例
     * @param centerPoint   中心点
     * @param list          方框坐标点
     */
    private void updateWaterMarkPositionOnXAndY(float xScaleDregree, float yScaleDregree, PointF centerPoint, List<PointF> list) {
        float width = Math.abs(list.get(0).x - list.get(3).x);
        float height = Math.abs(list.get(0).y - list.get(1).y);
        float x0 = centerPoint.x - width / 2 * xScaleDregree;
        float x1 = centerPoint.x + width / 2 * xScaleDregree;
        float y0 = centerPoint.y - height / 2 * yScaleDregree;
        float y1 = centerPoint.y + height / 2 * yScaleDregree;
        refreshWaterMarkByFourPoint(x0, x1, y0, y1);
    }


    /**
     * @param scaleDregree 缩放比例；scaling ratio
     * @param centerPoint  中心坐标；Center coordinates
     * @param angle        旋转角度；Rotation angle
     * @param list         方框坐标点；Box coordinates
     */
    private void updateWaterMarkPositionOnScaleAndRotate(float scaleDregree, PointF centerPoint, float angle, List<PointF> list) {
        float width = Math.abs(list.get(0).x - list.get(3).x);
        float height = Math.abs(list.get(0).y - list.get(1).y);
        float x0 = centerPoint.x - width / 2 * scaleDregree;
        float x1 = centerPoint.x + width / 2 * scaleDregree;
        float y0 = centerPoint.y - height / 2 * scaleDregree;
        float y1 = centerPoint.y + height / 2 * scaleDregree;
        refreshWaterMarkByFourPoint(x0, x1, y0, y1);
    }

    /**
     * 四个点就能确定一个矩形 (x0,y0) (x0,y1) (x1,y1) (x1,y0)
     * Four points can determine a rectangle (x0, y0) (x0, y1) (x1, y1) (x1, y0)
     */
    private void refreshWaterMarkByFourPoint(float x0, float x1, float y0, float y1) {
        List<PointF> newList = setFourPointToList(x0, x1, y0, y1);
        if (checkInLiveWindow(newList)) {
            mDrawRect.setDrawRect(newList, EDIT_MODE_WATERMARK);
//            if (waterMarkChangeListener != null) {
//                waterMarkChangeListener.onScaleAndRotate(newList);
//            }
        }
    }

    /**
     * @param scaleDregree 缩放比例；scaling ratio
     * @param centerPoint  中心坐标；Center coordinates
     * @param angle        旋转角度；Rotation angle
     * @param list         方框坐标点；Box coordinates
     */
    private void updateEffectPositionOnScaleAndRotate(float scaleDregree, PointF centerPoint, float angle, List<PointF> list) {
        float width = Math.abs(list.get(0).x - list.get(3).x);
        float height = Math.abs(list.get(0).y - list.get(1).y);
        float x0 = centerPoint.x - width / 2 * scaleDregree;
        float x1 = centerPoint.x + width / 2 * scaleDregree;
        float y0 = centerPoint.y - height / 2 * scaleDregree;
        float y1 = centerPoint.y + height / 2 * scaleDregree;
        refreshEffectByFourPoint(x0, x1, y0, y1);
    }

    /**
     * 四个点就能确定一个矩形 (x0,y0) (x0,y1) (x1,y1) (x1,y0)
     * Four points can determine a rectangle (x0, y0) (x0, y1) (x1, y1) (x1, y0)
     */
    private void refreshEffectByFourPoint(float x0, float x1, float y0, float y1) {
        List<PointF> newList = setFourPointToList(x0, x1, y0, y1);
        if (checkInLiveWindow(newList)) {
            mDrawRect.setDrawRect(newList, EDIT_MODE_EFFECT);
//            if (waterMarkChangeListener != null) {
//                waterMarkChangeListener.onScaleAndRotate(newList);
//            }
        }
    }

    /**
     * 更新组合字幕在视图上的坐标
     * Update the coordinates of the combined subtitles on the view
     */
    public void updateCompoundCaptionCoordinate(NvsTimelineCompoundCaption caption) {
        if (caption != null) {
            /*
             * 获取字幕的原始包围矩形框变换后的顶点位置
             * Get the transformed vertex position of the original bounding rectangle of the caption
             * */
            List<PointF> list = caption.getCompoundBoundingVertices(NvsTimelineCompoundCaption.BOUNDING_TYPE_FRAME);
            if (list == null || list.size() < 4) {
                return;
            }

            List<PointF> newList = getAssetViewVerticesList(list);
            List<List<PointF>> newSubCaptionList = new ArrayList<>();
            int subCaptionCount = caption.getCaptionCount();
            for (int index = 0; index < subCaptionCount; index++) {
                List<PointF> subList = caption.getCaptionBoundingVertices(index, NvsTimelineCompoundCaption.BOUNDING_TYPE_TEXT);
                if (subList == null || subList.size() < 4) {
                    continue;
                }
                List<PointF> newSubList = getAssetViewVerticesList(subList);
                newSubCaptionList.add(newSubList);
            }
            mDrawRect.setCompoundDrawRect(newList, newSubCaptionList, Constants.EDIT_MODE_COMPOUND_CAPTION);
        }
    }

    private List<PointF> getAssetViewVerticesList(List<PointF> verticesList) {
        List<PointF> newList = new ArrayList<>();
        for (int i = 0; i < verticesList.size(); i++) {
            PointF pointF = mLiveWindow.mapCanonicalToView(verticesList.get(i));
            newList.add(pointF);
        }
        return newList;
    }


    /**
     * 四个点坐标转化到list，从左上逆时针
     * Four point coordinates converted to list, counterclockwise from top left
     */
    private List<PointF> setFourPointToList(float x0, float x1, float y0, float y1) {
        List<PointF> newList = new ArrayList<>();
        newList.add(new PointF(x0, y0));
        newList.add(new PointF(x0, y1));
        newList.add(new PointF(x1, y1));
        newList.add(new PointF(x1, y0));
        return newList;
    }

    public float[] getFxData() {
        float[] floats = new float[8];
        List<PointF> drawRect = mDrawRect.getDrawRect();
        floats[0] = mLiveWindow.mapViewToNormalized(drawRect.get(0)).x;
        floats[1] = mLiveWindow.mapViewToNormalized(drawRect.get(0)).y;
        floats[2] = mLiveWindow.mapViewToNormalized(drawRect.get(1)).x;
        floats[3] = mLiveWindow.mapViewToNormalized(drawRect.get(1)).y;
        floats[4] = mLiveWindow.mapViewToNormalized(drawRect.get(2)).x;
        floats[5] = mLiveWindow.mapViewToNormalized(drawRect.get(2)).y;
        floats[6] = mLiveWindow.mapViewToNormalized(drawRect.get(3)).x;
        floats[7] = mLiveWindow.mapViewToNormalized(drawRect.get(3)).y;
        return floats;
    }

    /**
     * 坐标集合是否在liveWindow内
     * Whether the coordinate collection is in the liveWindow
     *
     * @param newList
     * @return
     */
    private boolean checkInLiveWindow(List<PointF> newList) {
        if (pointFListLiveWindow != null) {
            float minX = pointFListLiveWindow.get(0).x;
            float maxX = pointFListLiveWindow.get(2).x;
            float minY = pointFListLiveWindow.get(0).y;
            float maxY = pointFListLiveWindow.get(2).y;
            for (PointF pointF : newList) {
                if (pointF.x < minX || pointF.x > maxX || pointF.y < minY || pointF.y > maxY) {
                    LogUtils.e(TAG, "checkInLiveWindow " + minX + "       " + pointF.x + "      " + maxX);
                    LogUtils.e(TAG, "checkInLiveWindow " + minY + "       " + pointF.y + "      " + maxY);
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 拖动过程中更新水印DrawRect的位置
     * Update the position of the waterMark DrawRect during the drag process
     *
     * @param x
     * @param y
     * @param list
     */
    private void updateWaterMarkPositionOnDrag(float x, float y, List<PointF> list) {
        List<PointF> newList = new ArrayList<>();
        for (PointF pointF : list) {
            newList.add(new PointF(pointF.x + x, pointF.y - y));
        }
        updateInLiveWindow(newList);
        mDrawRect.setDrawRect(newList, EDIT_MODE_WATERMARK);
//        if (waterMarkChangeListener != null) {
//            waterMarkChangeListener.onDrag(newList);
//        }
    }

    /**
     * 拖动过程中更新效果DrawRect的位置
     * Update the position of the effect DrawRect during the drag process
     *
     * @param x
     * @param y
     * @param list
     */
    private void updateEffectPositionOnDrag(float x, float y, List<PointF> list) {
        List<PointF> newList = new ArrayList<>();
        for (PointF pointF : list) {
            newList.add(new PointF(pointF.x + x, pointF.y - y));
        }
        updateInLiveWindow(newList);
        mDrawRect.setDrawRect(newList, EDIT_MODE_EFFECT);
//        if (waterMarkChangeListener != null) {
//            waterMarkChangeListener.onDrag(newList);
//        }
    }

    private void clearAllWaterMarkData(int waterMarkType) {
        if (waterMarkType == WATERMARKTYPE_STATIC) {
            clearSelectedWaterMarkData();
        }
        TimelineUtil.checkAndDeleteExitFX(mTimeline);
        if (waterMarkType == WATERMARKTYPE_EFFECK) {
            removeWaterToTimeline();
        }
    }


    /**
     * 更新坐标(边界检测，出了边界后挪回)
     * Update coordinates (boundary detection, move back after the boundary is out)
     *
     * @param newList
     */
    private void updateInLiveWindow(List<PointF> newList) {
        if (checkInLiveWindow(newList)) {
            return;
        }
        if (pointFListLiveWindow != null) {
            float minX = pointFListLiveWindow.get(0).x;
            float maxX = pointFListLiveWindow.get(2).x;
            float minY = pointFListLiveWindow.get(0).y;
            float maxY = pointFListLiveWindow.get(2).y;
            if (newList != null && newList.size() >= 4) {
                float dx = 0, dy = 0;
                //4个点左上，左下，右下，右上
                if (newList.get(0).x < minX) {
                    dx = newList.get(0).x - minX;
                }
                if (newList.get(3).x > maxX) {
                    dx = newList.get(3).x - maxX;
                }
                if (newList.get(0).y < minY) {
                    dy = newList.get(0).y - minY;
                }
                if (newList.get(1).y > maxY) {
                    dy = newList.get(1).y - maxY;
                }
                for (PointF pointF : newList) {
                    //出去了，移回
                    pointF.x -= dx;
                    pointF.y -= dy;
                }
            }

        }
    }

    /**
     * 第一次添加水印
     * Add watermark for the first time
     *
     * @param w livewindow宽度；livewindow width
     * @param h livewindow高度；livewindow height
     */
    public void firstSetWaterMarkPosition(int w, int h, String filePath) {
        setPointFListLiveWindow(w, h);
        Point point = getPictureSize(filePath);
        int defaultWidth = point.x;
        int defaultHeight = point.y;
        int x0 = w - defaultWidth;
        int x1 = w;
        int y0 = 0;
        List<PointF> newList = setFourPointToList(x0, x1, y0, defaultHeight);
        pointFListToFirstAddWaterMark = newList;
        /*
         * 初始位置需将操作框按钮都放置在画面内
         * The initial position needs to place the operation box buttons in the screen
         * */
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.delete);
        int bitmapHeight = bitmap.getHeight();
        int bitmapWidth = bitmap.getWidth();
        bitmap.recycle();
        x0 = x0 - bitmapWidth / 2;
        x1 = x1 - bitmapWidth / 2;
        defaultHeight = defaultHeight + bitmapHeight / 2;
        y0 = y0 + bitmapHeight / 2;

        newList = setFourPointToList(x0, x1, y0, defaultHeight);
        mDrawRect.setDrawRect(newList, EDIT_MODE_WATERMARK);
    }

    /**
     * 根据宽高获取liveWindow的四个角坐标
     * Get the four corner coordinates of livewindow according to the width and height
     */
    private void setPointFListLiveWindow(int w, int h) {
//        int x0 = Math.abs(w - h) / 2;
        int x0 = 0;
        int x1 = w;
        int y0 = 0;
        int y1 = h;
        LogUtils.e(TAG, "liveWindow的四个角坐标  " + x0 + "  " + x1 + "  " + y0 + "  " + y1);
        pointFListLiveWindow = setFourPointToList(x0, x1, y0, y1);
    }

    public Point getPictureSize(String filePath) {
        Point point = mDrawRect.getPicturePoint(filePath);
        int defaultWidth = (int) getResources().getDimension(R.dimen.edit_waterMark_width);
        int defaultHeight = (int) getResources().getDimension(R.dimen.edit_waterMark_height);
        if (point != null) {
            defaultHeight = defaultWidth * point.y / point.x;
        }
        return new Point(defaultWidth, defaultHeight);
    }

    private void clearSelectedWaterMarkData() {
        mWaterMarkPicture = null;
        mPicturePath = null;
    }

    private void setWaterMarkData(int type, String picturePath, String mWaterMarkPath) {
        mPicturePath = picturePath;
        mWaterMarkPicture = mWaterMarkPath;
        waterMarkType = type;
        Point point;
        if (waterMarkType == WATERMARKTYPE_STATIC) {
            point = getPictureSize(picturePath);
        } else {
            point = getPictureSize(mWaterMarkPath);
        }
        mPictureW = point.x;
        mPictureH = point.y;
    }

    private void setWaterMarkOnTheFirst(String mWaterMarkPicture) {
        mEditMode = EDIT_MODE_WATERMARK;
        mDrawRect.setPicturePath(mWaterMarkPicture);
        firstSetWaterMarkPosition(mLiveWindow.getWidth(), mLiveWindow.getHeight(), mWaterMarkPicture);
        mDrawRect.setVisibility(View.VISIBLE);
    }


    private void initMaskView() {
        mMaskControlView = findViewById(R.id.maskControlView);
        zoomView = findViewById(R.id.mask_zoom);
        maskView = findViewById(R.id.mask_view);
        zoomView.setMaskView(maskView);
        zoomView.post(() -> zoomView.setVideoFragmentHeight(mPlayerLayout.getHeight(), mLiveWindow.getWidth(), mLiveWindow.getHeight()));
        mMaskControlView.setOnFunctionListener(new MaskControlView.OnFunctionListener() {
            @Override
            public void onMaskSelected(MaskBean maskBean) {
                zoomView.setMaskTypeAndInfo(maskBean.getMaskType(), maskInfoDataMap.get(mCurrentPos));
                maskInfoDataMap.put(mCurrentPos, zoomView.getMaskInfoData());
            }

            @Override
            public void onMaskReserve() {
                if (mCurrentPos >= 0 && mClipInfoArray != null && mClipInfoArray.size() > mCurrentPos) {
                    ClipInfo clipInfo = mClipInfoArray.get(mCurrentPos);
                    if (clipInfo != null) {
                        MaskInfoData maskInfoData = zoomView.getMaskInfoData();
                        if (maskInfoData != null) {
                            maskInfoData.setReverse(!maskInfoData.isReverse());
                            applyMaskInfo(clipInfo, maskInfoData);
                        }
                    }
                }
            }

            @Override
            public void onMaskConfirm() {
                mMaskControlView.setVisibility(View.GONE);
                zoomView.setVisibility(View.GONE);
                if (mClipInfoArray != null && maskInfoDataMap != null) {
                    for (int i = 0; i < mClipInfoArray.size(); i++) {
                        mClipInfoArray.get(i).setMaskInfoData(maskInfoDataMap.get(i));
                    }
                }
                TimelineData.instance().setClipInfoData(mClipInfoArray);
                TimelineUtil.reBuildVideoTrack(mTimeline);
                mVideoTrack = mTimeline.getVideoTrackByIndex(0);
            }
        });
        zoomView.setOnDataChangeListener(new ZoomView.OnDataChangeListener() {
            @Override
            public void onDataChanged() {
                if (mClipInfoArray.size() == 0) {
                    return;
                }
                if (mCurrentPos < 0) {
                    return;
                }
                ClipInfo clipInfo = mClipInfoArray.get(mCurrentPos);
                if (clipInfo == null) {
                    return;
                }
                setMaskCenter(clipInfo);
                applyMaskInfo(clipInfo, zoomView.getMaskInfoData());
            }

            @Override
            public void onMaskTextClick() {

            }
        });
    }

    /**
     * 设置MaskView的中心点
     * Set the center point of MaskView
     *
     * @param clipInfo
     */
    private void setMaskCenter(ClipInfo clipInfo) {
        if (clipInfo == null) return;
        BackGroundInfo backGroundInfo = clipInfo.getBackGroundInfo();
        float transX = 0, transY = 0, scaleX = 1, rotation = 0;
        NvsVideoResolution videoResolution = TimelineData.instance().getVideoResolution();
        if (backGroundInfo != null && videoResolution != null) {
            transX = backGroundInfo.getTransX() / videoResolution.imageWidth * mLiveWindow.getWidth();
            transY = backGroundInfo.getTransY() / videoResolution.imageHeight * mLiveWindow.getHeight();
            Log.d(TAG, "=-= setMaskCenter transX:" + backGroundInfo.getTransX() + "transY:" + backGroundInfo.getTransY());
            scaleX = backGroundInfo.getScaleX();
            rotation = backGroundInfo.getRotation();
        }
        zoomView.setBackgroundInfo(transX, transY, rotation, scaleX);
    }

    /**
     * 应用蒙版
     * applyMaskInfo
     *
     * @param clipInfo
     */
    private void applyMaskInfo(ClipInfo clipInfo, MaskInfoData maskInfoData) {
        BackGroundInfo backGroundInfo = clipInfo.getBackGroundInfo();
        float transX = 0, transY = 0, scaleX = 1, rotation = 0;
        if (backGroundInfo != null) {
            transX = backGroundInfo.getTransX();
            transY = backGroundInfo.getTransY();
            scaleX = backGroundInfo.getScaleX();
            rotation = backGroundInfo.getRotation();
        }
        NvMaskHelper.buildRealMaskInfoData(maskInfoData, mLiveWindow, rotation,
                transX, transY, scaleX, clipInfo.getFileRatio());
        NvsVideoTrack videoTrack = mTimeline.getVideoTrackByIndex(0);
        NvsVideoClip videoClip = null;
        int clipCount = videoTrack.getClipCount();
        for (int i = 0; i < clipCount; i++) {
            NvsVideoClip clipByIndex = videoTrack.getClipByIndex(i);
            if (clipByIndex != null && i == mCurrentPos) {
                videoClip = clipByIndex;
            }
        }
        if (videoClip == null) {
            return;
        }
        TimelineUtil.applyMask(videoClip, maskInfoData);
        seekTimeline(NvsStreamingContext.getInstance().getTimelineCurrentPosition(mTimeline), 0);
    }


    private void controllerOperation() {
        mPlayButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (getCurrentEngineState() == NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
                    stopEngine();
                    /*
                     * 更新播放进度条显示标识
                     * Update the playback progress bar indicator
                     * */
                    if (mPlayBarVisibleState) {
                        mPlayStartFlag = -1;
                    }
                } else {
                    if (mTimeline == null) {
                        return;
                    }
                    //播放进度，如果是动画的页面，播放的可能是片段或者是整体，指定了播放的范围
                    long startTime = mStreamingContext.getTimelineCurrentPosition(mTimeline);
                    long endTime = mTimeline.getDuration();
                    if (mIsAnimationView || mIsBackgroundView) {
                        endTime = mPlayEndPoint;
                        if (startTime < mPlayStartPoint) {
                            startTime = mPlayStartPoint;
                        }
                    }
                    playVideo(startTime, endTime);
                    /*
                     * 更新播放进度条显示标识
                     * Update the playback progress bar indicator
                     * */
                    if (mPlayBarVisibleState) {
                        mPlayStartFlag = mStreamingContext.getTimelineCurrentPosition(mTimeline);
                    }
                }
            }
        });
//
//        mVoiceButton.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                if (mVideoVolumeListener != null) {
//                    mVideoVolumeListener.onVideoVolume();
//                }
//            }
//        });

//        mLiveWindow.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                /*
//                 * 如果正在录音，禁止操作
//                 * Prohibit operation if recording
//                 * */
//                if (mRecording) {
//                    return;
//                }
//                mPlayButton.callOnClick();
//            }
//        });
    }

    private void setLiveWindowRatio(int ratio, int titleHeight, int bottomHeight) {
        ViewGroup.LayoutParams layoutParams = mPlayerLayout.getLayoutParams();
        int statusHeight = ScreenUtils.getStatusBarHeight(this);
        int screenWidth = ScreenUtils.getScreenWidth(this);
        int screenHeight = ScreenUtils.getScreenHeight(this);
        int newHeight = screenHeight - titleHeight - bottomHeight - statusHeight;
        switch (ratio) {
            case NvAsset.AspectRatio_16v9: // 16:9
                layoutParams.width = screenWidth;
                layoutParams.height = (int) (screenWidth * 9.0 / 16);
                break;
            case NvAsset.AspectRatio_1v1: //1:1
                layoutParams.width = screenWidth;
                layoutParams.height = screenWidth;
                if (newHeight < screenWidth) {
                    layoutParams.width = newHeight;
                    layoutParams.height = newHeight;
                }
                break;
            case NvAsset.AspectRatio_9v16: //9:16
                layoutParams.width = (int) (newHeight * 9.0 / 16);
                layoutParams.height = newHeight;
                break;
            case NvAsset.AspectRatio_3v4: // 3:4
                layoutParams.width = (int) (newHeight * 3.0 / 4);
                layoutParams.height = newHeight;
                break;
            case NvAsset.AspectRatio_4v3: //4:3
                layoutParams.width = screenWidth;
                layoutParams.height = (int) (screenWidth * 3.0 / 4);
                break;
            case NvAsset.AspectRatio_21v9: //21:9
                layoutParams.width = screenWidth;
                layoutParams.height = (int) (screenWidth * 9.0 / 21);
                break;
            case NvAsset.AspectRatio_9v21: //9:21
                layoutParams.width = (int) (newHeight * 9.0 / 21);
                layoutParams.height = newHeight;
                break;
            case NvAsset.AspectRatio_18v9: //18:9
                layoutParams.width = screenWidth;
                layoutParams.height = (int) (screenWidth * 9.0 / 18);
                break;
            case NvAsset.AspectRatio_9v18: //9:18
                layoutParams.width = (int) (newHeight * 9.0 / 18);
                layoutParams.height = newHeight;
                break;
            default: // 16:9
                layoutParams.width = screenWidth;
                layoutParams.height = (int) (screenWidth * 9.0 / 16);
                break;
        }
        mPlayerLayout.setLayoutParams(layoutParams);
        mLiveWindow.setFillMode(NvsLiveWindow.FILLMODE_PRESERVEASPECTFIT);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    private void initTimeLineEditor() {
        mTimelineEditor.setSequencLeftPadding(halfScreenWidth);
        mTimelineEditor.setSequencRightPadding(ScreenUtils.getScreenWidth(this) - halfScreenWidth);
        mTimelineEditor.setTimeSpanLeftPadding(halfScreenWidth);
        mTimelineEditor.ZoomInSequence(1.5);
        mTimelineEditor.setOnScrollListener(this::updateCaptionRelativeView);
        mTimelineEditor.setSpanExtSelectListener(new NvsTimelineEditor.OnSpanExtSelectListener() {
            @Override
            public void onSpanSelect(NvsTimelineTimeSpanExt spanExt) {
                bottomEditView.showEditType();
                mCurrentPos = spanExt.getIndex();
                BackupData.instance().setClipIndex(mCurrentPos);
                long currentPosition = mStreamingContext.getTimelineCurrentPosition(mTimeline);
                if (currentPosition > spanExt.getInPoint() && currentPosition < spanExt.getOutPoint()) {
                    return;
                }
                int start = Math.round((spanExt.getInPoint() / (float) mTimeline.getDuration() * mTimelineEditor.getSequenceWidth()));
                mMultiSequenceView.scrollTo(start, 0);
                //todo:选中片段，底部操作栏变换为编辑选项
            }

            @Override
            public void onSpanMove(long timeStamp) {
                updateCaptionRelativeView(timeStamp);
//                if (mMultiSequenceView != null) {
//                    mMultiSequenceView.setThumbnailSequenceDescArray(getSequenceArray());
//                }
            }
        });
        mTimelineEditor.setMuteSelectListener(() -> {
            mVideoIsMute = !mVideoIsMute;
            mTimelineEditor.setMute(mVideoIsMute);
            if (mVideoIsMute) {
                mVideoTrack.setVolumeGain(muteVolumeValue, muteVolumeValue);
            } else {
                mVideoTrack.setVolumeGain(defaultVolumeValue, defaultVolumeValue);
            }
        });
        initMultiSequence();
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    private void initMultiSequence() {
        ArrayList<MultiThumbnailSequenceView.ThumbnailSequenceDesc> sequenceArray = getSequenceArray();
        Log.e("获取内容", "kk" + sequenceArray.size());
        long duration = mTimeline.getDuration();
        mTimelineEditor.initTimelineEditor(sequenceArray, duration);
        mTimelineEditor.addTimeSpanExt(sequenceArray, mClipInfoArray);
        mVideoIsMute = mVideoTrack.getVolumeGain().leftVolume == 0;
        mTimelineEditor.setMute(mVideoIsMute);

        RelativeLayout line = findViewById(R.id.view_line);
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) line.getLayoutParams();
        layoutParams.setMarginStart(halfScreenWidth + mTimelineEditor.m_timeTextWidth);

        mMultiSequenceView = mTimelineEditor.getMultiThumbnailSequenceView();

//        mMultiSequenceView

        mTimelineEditor.setOnScrollListener(this::updateCaptionRelativeView);
//        mTimelineEditor.setSpanExtSelectListener(spanExt -> {
//            long currentPosition = mStreamingContext.getTimelineCurrentPosition(mTimeline);
//            if (currentPosition > spanExt.getInPoint() && currentPosition < spanExt.getOutPoint()) {
//                return;
//            }
//            int start = Math.round((spanExt.getInPoint() / (float) mTimeline.getDuration() * mTimelineEditor.getSequenceWidth()));
//            mMultiSequenceView.scrollTo(start, 0);
//            //todo:选中片段，底部操作栏变换为编辑选项
//        });
        mTimelineEditor.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                Log.e("长按","长安" );
                return false;
            }
        });
        mMultiSequenceView.setOnTouchListener((v, event) -> {
//            gestureDetector.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
//                    mTimelineEditor.unSelectAllTimeSpan();
                    x = event.getRawX();
                    y = event.getRawY();
                    isLongClick=false;
                    runnable=new Runnable() {
                        @Override
                        public void run() {
                            isLongClick=true;
                        }
                    };
                    mhandler.postDelayed(runnable, 1500);
                    Log.e("看看啊","666");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e("看看啊","777");
                    if (isLongClick) {
                        if (Math.abs(event.getRawX() - x) < 10 && Math.abs(event.getRawY() - y) < 10) {
                            try {
                                Field field = View.class.getDeclaredField("mListenerInfo");
                                field.setAccessible(true);
                                Object object = field.get(v);
                                field = object.getClass().getDeclaredField("mOnClickListener");
                                field.setAccessible(true);
                                object = field.get(object);
                                if (object != null && object instanceof View.OnClickListener) {
                                    ((View.OnClickListener) object).onClick(v);
                                }
                            } catch (Exception e) {

                            }
                        } else {
                            Log.i("mandroid.cn", "button已移动");
                        }
                        isLongClick=false;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    mhandler.removeCallbacks(runnable);
                    return true;
            }
            mIsSeekTimeline = true;
            return false;
        });
        mMultiSequenceView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ViewGroup.LayoutParams layoutParams1 = mTimelineEditor.getLayoutParams();
                DragSortView dragSortView=new DragSortView(VideoEditActivity.this,VideoEditActivity.this);
                dragSortView.setData(TimelineData.instance().cloneClipInfoData());
                dragSortView.setLayoutParams(layoutParams1);
                timeline_edit_layout.addView(dragSortView);
            }
        });


    }

    private ArrayList<MultiThumbnailSequenceView.ThumbnailSequenceDesc> getSequenceArray() {
        int clipCount = mVideoTrack.getClipCount();
        ArrayList<MultiThumbnailSequenceView.ThumbnailSequenceDesc> sequenceDescsArray = new ArrayList<>();
        for (int index = 0; index < clipCount; ++index) {
            NvsVideoClip videoClip = mVideoTrack.getClipByIndex(index);
            if (videoClip == null) {
                continue;
            }
            MultiThumbnailSequenceView.ThumbnailSequenceDesc sequenceDescs = new MultiThumbnailSequenceView.ThumbnailSequenceDesc();
            sequenceDescs.mediaFilePath = videoClip.getFilePath();
            sequenceDescs.trimIn = videoClip.getTrimIn();
            sequenceDescs.trimOut = videoClip.getTrimOut();
            sequenceDescs.inPoint = videoClip.getInPoint();
            sequenceDescs.outPoint = videoClip.getOutPoint();
            sequenceDescs.stillImageHint = false;
            sequenceDescsArray.add(sequenceDescs);
        }
        return sequenceDescsArray;
    }

    private double getPixelMicrosecond(long duration) {
        int width = ScreenUtils.getScreenWidth(this);
        int leftPadding = ScreenUtils.dip2px(this, 13);
        int sequenceWidth = width - 2 * leftPadding;
        double pixelMicrosecond = sequenceWidth / (double) duration;
        return pixelMicrosecond;
    }


    /**
     * 更新时间线指定位置上的字幕数据和相关视图
     * Updates the caption data and associated views at the specified timestamp on the timeline
     *
     * @param timeStamp
     */
    private void updateCaptionRelativeView(long timeStamp) {
        if (timeStamp < 0) {
            return;
        }
        if (!mIsSeekTimeline) {
            return;
        }
        if (mTimeline != null) {
            updateCurPlayTime(timeStamp);
            seekTimeline(timeStamp, NvsStreamingContext.STREAMING_ENGINE_SEEK_FLAG_SHOW_CAPTION_POSTER);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopEngine();
    }

    @SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.iv_edit_close:
                finish();
                break;
            case R.id.iv_add_video:
                Bundle bundle = new Bundle();
                bundle.putInt("visitMethod", Constants.FROMCLIPEDITACTIVITYTOVISIT);
                BackupData.instance().clearAddClipInfoList();
                AppManager.getInstance().jumpActivityForResult(this, SelectMediaActivity.class, bundle, ADDVIDEO_REQUESTCODE);
                break;
            case R.id.iv_full_screen:
                AppManager.getInstance().jumpActivity(this, PreviewActivity.class);
                break;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != RESULT_OK) {
            return;
        }
        if (data == null) {
            return;
        }
        if (requestCode == ADDWATERMARK_REQUESTCODE) {
            clearAllWaterMarkData(WATERMARKTYPE_STATIC);
            setWaterMarkData(WATERMARKTYPE_STATIC, data.getStringExtra(SINGLE_PICTURE_PATH), data.getStringExtra(SINGLE_PICTURE_PATH));
            setWaterMarkOnTheFirst(mWaterMarkPicture);
            refreshLiveWindowFrame();
            if (mWaterMarkView != null) {
                mWaterMarkView.addDataAndRefresh(mPicturePath);
            }
            return;
        }
        ArrayList<ClipInfo> addCipInfoList = BackupData.instance().getAddClipInfoList();

        if (addCipInfoList.size() > 0) {
            //此处是从媒体库在选择资源  ， 修改存储的动画的特效数据结构
//            addNewAnimationInfo(false, mAddVideoPostion, addCipInfoList.size());
            mClipInfoArray.addAll(mAddVideoPostion, addCipInfoList);
            BackupData.instance().clearAddClipInfoList();
            /*
             * 为新增的素材添加默认转场
             * Add default transitions for new material
             * */
            if (mTransitionInfoArray != null) {
                ArrayList<TransitionInfo> temp = new ArrayList<>();
                int maxTransitionCount = mClipInfoArray.size() - mTransitionInfoArray.size() - 1;
                for (int i = 0; i < maxTransitionCount; i++) {
                    TransitionInfo transitionInfo = new TransitionInfo();
                    temp.add(transitionInfo);
                }
                if (mAddVideoPostion <= mTransitionInfoArray.size()) {
                    mTransitionInfoArray.addAll(mAddVideoPostion, temp);
                }
            }
            TimelineData.instance().setClipInfoData(mClipInfoArray);
            TimelineData.instance().setTransitionInfoArray(mTransitionInfoArray);
        }
        TimelineUtil.reBuildVideoTrack(mTimeline);
        //设置调节的数据,从clipInfo中取出调节的值
        //有关顺序问题，由于操作蒙版时，没有把调整的效果代入，所以蒙版要加在调整裁剪之前，同理背景
        TimelineUtil.buildColorAdjustInfo(mTimeline, TimelineData.instance().cloneClipInfoData());
        TimelineUtil.buildAdjustCutInfo(mTimeline, TimelineData.instance().cloneClipInfoData());
        TimelineUtil.buildTimelineBackground(mTimeline, TimelineData.instance().getClipInfoData());
        TimelineUtil.buildTimelineMaskClipInfo(mTimeline, TimelineData.instance().getClipInfoData());
        //设置动画
        TimelineUtil.buildTimelineAnimation(mTimeline, TimelineData.instance().getClipInfoData());
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        updateClipInfo();
        refreshLiveWindowFrame();
        updateTotalDurationText();
        initMultiSequence();
    }

    public void refreshLiveWindowFrame() {
        if (playState != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }
    @Override
    public void finshCallBack() {
        Log.e("结果呢","6666");
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineData.instance().setTransitionInfoArray(mTransitionInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        //设置调节的数据,从clipInfo中取出调节的值
        //有关顺序问题，由于操作蒙版时，没有把调整的效果代入，所以蒙版要加在调整裁剪之前，同理背景
        TimelineUtil.buildColorAdjustInfo(mTimeline, TimelineData.instance().cloneClipInfoData());
        TimelineUtil.buildAdjustCutInfo(mTimeline, TimelineData.instance().cloneClipInfoData());
        TimelineUtil.buildTimelineBackground(mTimeline, TimelineData.instance().getClipInfoData());
        TimelineUtil.buildTimelineMaskClipInfo(mTimeline, TimelineData.instance().getClipInfoData());
        //设置动画
        TimelineUtil.buildTimelineAnimation(mTimeline, TimelineData.instance().getClipInfoData());
        refreshLiveWindowFrame();
        updateTotalDurationText();
        initMultiSequence();
    }
    private void refreshSpanEditor() {
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineData.instance().setTransitionInfoArray(mTransitionInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        updateClipInfo();
        initMultiSequence();
    }

    @Override
    public void back() {
        if (mTimelineEditor != null) {
            mTimelineEditor.unSelectAllTimeSpan();
        }
    }

    @Override
    public void edit() {
        mTimelineEditor.getCurrentSpan(mStreamingContext.getTimelineCurrentPosition(mTimeline));
    }

    @Override
    public void split() {
        long mSpiltPoint = mStreamingContext.getTimelineCurrentPosition(mTimeline);
        if (mCurrentPos > 0) {
            mSpiltPoint -= mVideoTrack.getClipByIndex(mCurrentPos - 1).getOutPoint();
        }
        ClipInfo currentClipInfo = mClipInfoArray.get(mCurrentPos);
        long trimIn = currentClipInfo.getTrimIn();
        long trimOut = currentClipInfo.getTrimOut();
//        if (trimOut - mSpiltPoint < Constants.NS_TIME_BASE || mSpiltPoint - trimIn < Constants.NS_TIME_BASE) {
//            Util.showDialog(VideoEditActivity.this, "提示", "不可分割小于1秒的素材");
//            return;
//        }
        if (mSpiltPoint < Constants.NS_TIME_BASE) {
            Util.showDialog(VideoEditActivity.this, "提示", "不可分割小于1秒的素材");
            return;
        }
        float speed = currentClipInfo.getSpeed();
        speed = speed <= 0 ? 1.0f : speed;
        long newSpiltPoint = (long) (mSpiltPoint * speed) + currentClipInfo.getTrimIn();
        ClipInfo clipInfoFst = currentClipInfo.clone();
        clipInfoFst.changeTrimOut(newSpiltPoint);
        ClipInfo clipInfoSec = currentClipInfo.clone();
        clipInfoSec.changeTrimIn(newSpiltPoint);
        mClipInfoArray.remove(mCurrentPos);
        mClipInfoArray.add(mCurrentPos, clipInfoSec);
        mClipInfoArray.add(mCurrentPos, clipInfoFst);
        if ((mTransitionInfoArray != null) && (!mTransitionInfoArray.isEmpty())) {
            if (mCurrentPos <= mTransitionInfoArray.size()) {
                mTransitionInfoArray.add(mCurrentPos, new TransitionInfo());
            }
        }
        Bitmap bitmap = Util.getBitmapFromClipInfo(this, clipInfoSec);
        BitmapData.instance().insertBitmap(mCurrentPos + 1, bitmap);
//        refreshSpanEditor();

        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineData.instance().setTransitionInfoArray(mTransitionInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        updateClipInfo();
        mTimelineEditor.addTimeSpanExt(getSequenceArray(), mClipInfoArray);
    }

    @Override
    public void speedStart(int type) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        bottomEditView.showSpeedPopup(mClipInfoArray.get(mCurrentPos), type);
    }

    @Override
    public void speedNormal(float speed, boolean changeVoice) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        mClipInfoArray.get(mCurrentPos).setSpeed(speed);
        mClipInfoArray.get(mCurrentPos).setKeepAudioPitch(changeVoice);
        refreshSpanEditor();
        updateTotalDurationText();
        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
        mTimelineEditor.getCurrentSpan((videoClip.getOutPoint() - videoClip.getInPoint()) / 2 + videoClip.getInPoint());
        playVideo(videoClip.getInPoint(), videoClip.getOutPoint());
//        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
//        if (videoClip == null)
//            return;
//        videoClip.changeSpeed(speed, changeVoice);
    }

    @Override
    public void editCurve(ChangeSpeedCurveInfo changeSpeedCurveInfo) {
        if (mCurrentPos < 0) {
            return;
        }
        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
        if (videoClip == null) return;
        stopEngine();
        if (speedCurveEditPopup == null) {
            speedCurveEditPopup = new SpeedCurveEditPopup(this);
        }
        speedCurveEditPopup.setData(changeSpeedCurveInfo, videoClip);
        speedCurveEditPopup.showPopupWindow();
        long mPlayTimestamp = mStreamingContext.getTimelineCurrentPosition(mTimeline);
        if (mPlayTimestamp == 0) {
            playVideo(0, videoClip.getTrimOut());
        } else {
//            long bzPosition = mPlayTimestamp;
//            if (!TextUtils.isEmpty(changeSpeedCurveInfo.speed)) {
//                bzPosition = videoClip.GetTimelinePosByClipPosCurvesVariableSpeed(bzPosition) + videoClip.getTrimIn();
//            }
            speedCurveEditPopup.updatePlayProgress(mPlayTimestamp);
        }
        speedCurveEditPopup.setOnFunctionListener(new SpeedCurveEditPopup.OnFunctionListener() {
            @Override
            public void onStopVideo() {
                stopEngine();
            }

            @Override
            public void onTimelineMove(long timePoint) {

            }

            @Override
            public void onActionUp(long timePoint) {
                NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
                playVideo(videoClip.getInPoint(), videoClip.getOutPoint());
            }

            @Override
            public void onSpeedChanged(ChangeSpeedCurveInfo speed, long timePoint) {
                ChangeSpeedCurveInfo curveSpeed = mClipInfoArray.get(mCurrentPos).getCurveSpeed();
                if (curveSpeed != null && curveSpeed.speed.equals(changeSpeedCurveInfo.speed)) {
                    return;
                }
                mClipInfoArray.get(mCurrentPos).setCurveSpeed(changeSpeedCurveInfo);
                refreshSpanEditor();
                updateTotalDurationText();
                mTimelineEditor.getCurrentSpan((videoClip.getOutPoint() - videoClip.getInPoint()) / 2 + videoClip.getInPoint());

            }
        });
        speedCurveEditPopup.setOnDismissListener(new BasePopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                speedStart(BottomEditTypeView.SPEED_CURVE);
            }
        });
    }

    @Override
    public void speedCurve(ChangeSpeedCurveInfo changeSpeedCurveInfo) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        ChangeSpeedCurveInfo curveSpeed = mClipInfoArray.get(mCurrentPos).getCurveSpeed();
        if (curveSpeed != null && curveSpeed.speed.equals(changeSpeedCurveInfo.speed)) {
            return;
        }
        mClipInfoArray.get(mCurrentPos).setCurveSpeed(changeSpeedCurveInfo);
        refreshSpanEditor();
        updateTotalDurationText();
        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
        mTimelineEditor.getCurrentSpan((videoClip.getOutPoint() - videoClip.getInPoint()) / 2 + videoClip.getInPoint());
        playVideo(videoClip.getInPoint(), videoClip.getOutPoint());
    }

    @Override
    public void volume(float volume) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        mClipInfoArray.get(mCurrentPos).setVolume(volume);
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void animation() {

    }

    @Override
    public void delete() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mClipInfoArray.size() == 1) {
            Util.showDialog(VideoEditActivity.this, "提示", "至少保留一个素材");
            return;
        }
        int clipCount = mClipInfoArray.size();
        if (mCurrentPos < 0 || mCurrentPos >= clipCount) {
            return;
        }
        /*
         * 删除素材和转场
         * delete material and transition
         * */
        mClipInfoArray.remove(mCurrentPos);

        if ((mTransitionInfoArray != null) && !mTransitionInfoArray.isEmpty() && mTransitionInfoArray.size() > mCurrentPos) {
            mTransitionInfoArray.remove(Math.max(mCurrentPos - 1, 0));
        }
        refreshSpanEditor();
    }

    @Override
    public void copy() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        int count = mClipInfoArray.size();
        if (mCurrentPos < 0 || mCurrentPos > count) {
            return;
        }
        mClipInfoArray.add(mCurrentPos, mClipInfoArray.get(mCurrentPos).clone());
        /*
         * 添加转场
         * Add transition
         * */
        if (mTransitionInfoArray != null && mTransitionInfoArray.size() >= mCurrentPos) {
            mTransitionInfoArray.add(mCurrentPos, new TransitionInfo());
        }
        refreshSpanEditor();
    }

    @Override
    public void reverse() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        boolean isReserve = mClipInfoArray.get(mCurrentPos).isReverse();
        mClipInfoArray.get(mCurrentPos).setReverse(!isReserve);
        refreshSpanEditor();
        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
        mTimelineEditor.getCurrentSpan((videoClip.getOutPoint() - videoClip.getInPoint()) / 2 + videoClip.getInPoint());
        playVideo(videoClip.getInPoint(), videoClip.getOutPoint());
    }

    @Override
    public void onHorizontal() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        int mScaleX = 1;
        ClipInfo clipInfo = mClipInfoArray.get(mCurrentPos);
        int scaleX = clipInfo.getScaleX();
        if (scaleX >= -1) {
            mScaleX = scaleX;
        }
        mClipInfoArray.get(mCurrentPos).setScaleX(mScaleX > 0 ? -1 : 1);
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void onRotation() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        int mRotateAngle = 0;
        ClipInfo clipInfo = mClipInfoArray.get(mCurrentPos);
        int rotateAngle = clipInfo.getRotateAngle();
        switch (rotateAngle) {
            case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_0:
                mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_90;
                break;
            case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_90:
                mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_180;
                break;
            case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_180:
                mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_270;
                break;
            case NvsVideoClip.ClIP_EXTRAVIDEOROTATION_270:
                mRotateAngle = NvsVideoClip.ClIP_EXTRAVIDEOROTATION_0;
                break;
            default:
                break;
        }
        mClipInfoArray.get(mCurrentPos).setRotateAngle(mRotateAngle);
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void adjustStart() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        NvsVideoClip videoClip = mVideoTrack.getClipByIndex(mCurrentPos);
        bottomEditView.showAdjustPopup(mClipInfoArray.get(mCurrentPos), videoClip);
    }

    @Override
    public void adjustChange(float colorVal, String mCurrentColorType) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        //亮度
        if (mCurrentColorType.equals(Constants.ADJUST_BRIGHTNESS)) {
            mClipInfoArray.get(mCurrentPos).setBrightnessVal(colorVal);
        }
        //对比度
        else if (mCurrentColorType.equals(Constants.ADJUST_CONTRAST)) {
            mClipInfoArray.get(mCurrentPos).setContrastVal(colorVal);
        }
        //饱和度
        else if (mCurrentColorType.equals(Constants.ADJUST_SATURATION)) {
            mClipInfoArray.get(mCurrentPos).setSaturationVal(colorVal);
        }
        //高光
        else if (mCurrentColorType.equals(Constants.ADJUST_HIGHTLIGHT)) {
            mClipInfoArray.get(mCurrentPos).setmHighLight(colorVal);
        }
        //阴影
        else if (mCurrentColorType.equals(Constants.ADJUST_SHADOW)) {
            mClipInfoArray.get(mCurrentPos).setmShadow(colorVal);
        }
        //色温
        else if (mCurrentColorType.equals(Constants.ADJUST_TEMPERATURE)) {
            mClipInfoArray.get(mCurrentPos).setTemperature(colorVal);
        }
        //色调
        else if (mCurrentColorType.equals(Constants.ADJUST_TINT)) {
            mClipInfoArray.get(mCurrentPos).setTint(colorVal);
        }
        //褪色
        else if (mCurrentColorType.equals(Constants.ADJUST_FADE)) {
            mClipInfoArray.get(mCurrentPos).setFade(colorVal);
        }
        //暗角
        else if (mCurrentColorType.equals(Constants.ADJUST_DEGREE)) {
            mClipInfoArray.get(mCurrentPos).setVignetteVal(colorVal);
        }
        //锐度
        else if (mCurrentColorType.equals(Constants.ADJUST_AMOUNT)) {
            mClipInfoArray.get(mCurrentPos).setSharpenVal(colorVal);
        }
        //噪点  程度 密度
        else if (mCurrentColorType.equals(Constants.ADJUST_DENOISE)) {
            mClipInfoArray.get(mCurrentPos).setDensity(colorVal);
        } else if (mCurrentColorType.equals(Constants.ADJUST_DENOISE_DENSITY)) {

            mClipInfoArray.get(mCurrentPos).setDenoiseDensity(colorVal);
        }
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            mStreamingContext.seekTimeline(mTimeline, mStreamingContext.getTimelineCurrentPosition(mTimeline), NvsStreamingContext.VIDEO_PREVIEW_SIZEMODE_LIVEWINDOW_SIZE, 0);
        }
    }

    @Override
    public void mask() {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        mMaskControlView.setVisibility(View.VISIBLE);
        zoomView.setVisibility(View.VISIBLE);
        for (int i = 0; i < mClipInfoArray.size(); i++) {
            ClipInfo clipInfoDatum = mClipInfoArray.get(i);
            String filePath = clipInfoDatum.getFilePath();
            NvsAVFileInfo avInfoFromFile = NvsStreamingContext.getAVInfoFromFile(filePath, 0);
            if (avInfoFromFile != null) {
                int videoWidth = avInfoFromFile.getVideoStreamDimension(0).width;
                int videoHeight = avInfoFromFile.getVideoStreamDimension(0).height;
                int height = videoHeight;
                int width = videoWidth;
                int videoStreamRotation = avInfoFromFile.getVideoStreamRotation(0);
                if (videoStreamRotation % 2 == 1) {
                    width = avInfoFromFile.getVideoStreamDimension(0).height;
                    height = avInfoFromFile.getVideoStreamDimension(0).width;
                }
                clipInfoDatum.setFileRatio(width * 1f / (height * 1f));
                maskInfoDataMap.put(i, clipInfoDatum.getMaskInfoData());
            }
        }
    }

    @Override
    public void canvasColor(boolean userAll, MultiColorInfo colorInfo) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        String colorValue = colorInfo.getColorValue();
        if (userAll) {
            for (int i = 0; i < mClipInfoArray.size(); i++) {
                mClipInfoArray.get(i).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_COLOR).setColorValue(colorValue);
                mClipInfoArray.get(i).setBackgroundValue(colorValue);
            }
        } else {
            mClipInfoArray.get(mCurrentPos).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_COLOR).setColorValue(colorValue);
            mClipInfoArray.get(mCurrentPos).setBackgroundValue(colorValue);
        }
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void canvasStyle(boolean userAll, int position, BackgroundStyleInfo backgroundStyleInfo) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        if (position == 0) {
            //素材选择 selectBackgroundResource
//            Bundle bundle = new Bundle();
//            bundle.putInt(Constants.SELECT_MEDIA_FROM, Constants.SELECT_PICTURE_FROM_BACKGROUND);
//            AppManager.getInstance().jumpActivityForResult(AppManager.getInstance().currentActivity(), SingleClickActivity.class, bundle, REQUEST_CODE_BACKGROUND);
            return;
        }

        if (position == 1) {
            NvsVideoResolution videoRes = mTimeline.getVideoRes();
            if (videoRes == null) {
                return;
            }
            mClipInfoArray.get(mCurrentPos).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_COLOR).setColorValue("#000000");
            mClipInfoArray.get(mCurrentPos).setBackgroundValue("#000000");
        } else {
            String filePath = backgroundStyleInfo.getFilePath();
            String fileDir = "assets:/background/image";
            BackGroundInfo backGroundInfo = mClipInfoArray.get(mCurrentPos).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_TYPE);
            backGroundInfo.setFilePath(fileDir + "/" + filePath);
            mClipInfoArray.get(mCurrentPos).setBackgroundValue(filePath);
        }
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void canvasVague(boolean userAll, float strength) {
        if (mClipInfoArray.size() == 0) {
            return;
        }
        if (mCurrentPos < 0) {
            return;
        }
        if (userAll) {
            for (int i = 0; i < mClipInfoArray.size(); i++) {
                mClipInfoArray.get(i).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_COLOR).setValue(strength);
                mClipInfoArray.get(i).setBackgroundValue(strength + "");
            }
        } else {
            mClipInfoArray.get(mCurrentPos).buildBackgroundInfo(BackGroundInfo.BackgroundType.BACKGROUND_BLUR).setValue(strength);
            mClipInfoArray.get(mCurrentPos).setBackgroundValue(strength + "");
        }
        TimelineData.instance().setClipInfoData(mClipInfoArray);
        TimelineUtil.reBuildVideoTrack(mTimeline);
        mVideoTrack = mTimeline.getVideoTrackByIndex(0);
        if (mStreamingContext.getStreamingEngineState() != NvsStreamingContext.STREAMING_ENGINE_STATE_PLAYBACK) {
            seekTimeline(mStreamingContext.getTimelineCurrentPosition(mTimeline), 0);
        }
    }

    @Override
    public void waterMark() {
        mTimeline.deleteWatermark();
        refreshLiveWindowFrame();
        WaterMarkData waterMarkData = TimelineData.instance().getWaterMarkData();
        if (waterMarkData != null) {
            setWaterMarkData(waterMarkData.getWaterMarkItemData().getItemWaterMarkType(), waterMarkData.getPicPath(), waterMarkData.getWaterMarkItemData().getWaterMarkpath());
            liveWindowPoint = waterMarkData.getPointOfLiveWindow();
            if (waterMarkType == WATERMARKTYPE_STATIC) {
                setWaterMarkOnTheFirst(mWaterMarkPicture);
            } else if (waterMarkType == WATERMARKTYPE_DYNAMICS) {
                mTransX = waterMarkData.getTransX();
                mTransY = waterMarkData.getTransY();
                mScale = waterMarkData.getScale();
//                setDynamicWaterMarkOnTheFirst(mPicturePath, mWaterMarkPicture, true);
            }
            mWaterType = WATER_MARK_TYPE;
            mDrawRect.setVisibility(View.VISIBLE);
            mDrawRect.setDrawRect(waterMarkData.getPointFInLiveWindow(), EDIT_MODE_WATERMARK);
        }
        mWaterMarkView.setVisibility(View.VISIBLE);
    }

    @Override
    public void effect() {
        mEffectView.setVisibility(View.VISIBLE);
    }
}